From 47f4b2e4547773895c53827f212ac4d9b93593ce Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Tue, 19 Jan 2016 23:56:03 +0100 Subject: [PATCH] Reduce code size a bit Do some changes that reduce the code size a bit. --- devices.c | 44 ++++----- log.c | 52 +++++------ mount.c | 239 ++++++++++++++++++++++++++++------------------- nfs.c | 136 ++++++++++++++------------- tiny_initramfs.c | 100 +++++++++++--------- 5 files changed, 307 insertions(+), 264 deletions(-) diff --git a/devices.c b/devices.c index bfa3927..1c891a7 100644 --- a/devices.c +++ b/devices.c @@ -94,9 +94,8 @@ void wait_for_device(char *real_device_name, int *timeout, const char *device, i * do a very simple and stupid polling loop to wait until the * requested device is present. This could be improved a bit, * but for now it's good enough. */ - time_t start; - struct timeval tv; - int r, have_device; + time_t start, current; + int have_device; static int have_shown_message_timeout; int type; unsigned int major, minor; @@ -104,19 +103,13 @@ void wait_for_device(char *real_device_name, int *timeout, const char *device, i /* Parse device information */ if (!is_valid_device_name(device, &type, &major, &minor, uuid)) - panic(0, LOG_PREFIX, "Unsupported device specified: ", device, NULL); + panic(0, "Unsupported device specified: ", device, NULL); - if (delay) { - struct timespec req = { delay, 0 }; - struct timespec rem; - (void)nanosleep(&req, &rem); - } + if (delay) + (void)sleep(delay); /* Our timeout starts *after* the rootdelay. */ - r = gettimeofday(&tv, NULL); - if (r < 0) - panic(errno, LOG_PREFIX, "Couldn't determine current time for timeout", NULL); - start = tv.tv_sec; + start = time(NULL); if (type == WANT_NAME) { set_buf(real_device_name, MAX_PATH_LEN, device, NULL); @@ -126,17 +119,15 @@ void wait_for_device(char *real_device_name, int *timeout, const char *device, i } while (have_device) { - r = gettimeofday(&tv, NULL); - if (r < 0) - panic(errno, LOG_PREFIX, "Couldn't determine current time for timeout", NULL); - if (*timeout > 0 && tv.tv_sec - start > *timeout) - panic(0, LOG_PREFIX, "Timeout while waiting for devices for / (and possibly /usr) filesystems to appear " - "(did you specify the correct ones?)", NULL); + current = time(NULL); + if (*timeout > 0 && current - start > *timeout) + panic(0, "Timeout while waiting for devices for / (and possibly /usr) filesystems to appear " + "(did you specify the correct ones?)", NULL); /* In case this takes longer, show a nice message so the user has SOME * idea of what's going on here. */ - if (tv.tv_sec - start > DEVICE_MESSAGE_TIMEOUT && !have_shown_message_timeout) { + if (current - start > DEVICE_MESSAGE_TIMEOUT && !have_shown_message_timeout) { have_shown_message_timeout = 1; - warn(LOG_PREFIX, "Waiting for ", device, " to appear...", NULL); + warn("Waiting for ", device, " to appear...", NULL); } /* Sleep for DEVICE_POLL_MSEC milliseconds, then poll again. */ struct timespec req = { 0, DEVICE_POLL_MSEC * 1000 * 1000 }; @@ -152,10 +143,8 @@ void wait_for_device(char *real_device_name, int *timeout, const char *device, i /* Make sure we record how many seconds on the timeout are left, * because this function may be called again for the /usr filesystem. */ if (*timeout > 0) { - r = gettimeofday(&tv, NULL); - if (r < 0) - panic(errno, LOG_PREFIX, "Couldn't determine current time for timeout", NULL); - *timeout = tv.tv_sec - start; + current = time(NULL); + *timeout = current - start; if (*timeout <= 0) *timeout = 1; } @@ -265,12 +254,11 @@ int is_valid_device_name(const char *device_name, int *type, unsigned int* major if (device_name[0] == '0' && device_name[1] == 'x') { x = strtoul(device_name + 2, &endptr, 16); if (endptr && !*endptr) { - if (type) + if (type) { *type = WANT_MAJMIN; - if (major) *major = (int)(x >> 8); - if (minor) *minor = (int)(x & 0xff); + } return 1; } return 0; diff --git a/log.c b/log.c index 9b0a9fe..4f1c136 100644 --- a/log.c +++ b/log.c @@ -43,7 +43,7 @@ void panic(int err, ...) * because a kernel panic obscures the message. But we need to cause * a kernel panic (by PID 1 exiting), because if the user tells the * kernel to reboot on panic, we want to make sure this happens. */ - warn(LOG_PREFIX, "Will cause kernel panic in 10s...", NULL); + warn("Will cause kernel panic in 10s...", NULL); sleep(10); _exit(1); } @@ -61,40 +61,25 @@ void showmsgv(va_list ap, const char *before1, const char *after1, const char *a /* Don't use stdio functions, because we link statically * and they bloat the binary. */ - va_list ap_count; - int argc = 0; - int i; + int argc = 1; int fd; struct iovec iov[32]; - unsigned extra_arg_count = !!before1 + !!after1 + !!after2; - unsigned first_arg = 0; - - va_copy(ap_count, ap); - while (va_arg(ap_count, const char *)) { - argc++; - } - va_end(ap_count); - - /* Try to open /dev/kmsg, log to stderr if not possible */ - fd = open(KMSG_FILENAME, O_WRONLY | O_NOCTTY | O_CLOEXEC); - if (fd < 0) - fd = 2; - - /* We only support a fixed number of arguments arguments. */ - if (argc + 1 + extra_arg_count > 32) - argc = 31 - extra_arg_count; + unsigned extra_arg_count = !!after1 + !!after2; + const char *arg; if (before1) { - iov[0].iov_base = (char *)before1; - iov[0].iov_len = strlen(before1); + iov[1].iov_base = (char *)before1; + iov[1].iov_len = strlen(before1); argc++; - first_arg = 1; } - for (i = first_arg; i < argc; i++) { - const char *arg = va_arg(ap, const char *); - iov[i].iov_base = (char *)arg; - iov[i].iov_len = strlen(arg); + while ((arg = va_arg(ap, const char *))) { + iov[argc].iov_base = (char *)arg; + iov[argc].iov_len = strlen(arg); + argc++; + /* We only support a fixed number of arguments arguments. */ + if (argc + 1 + extra_arg_count > 32) + break; } if (after1) { @@ -109,9 +94,20 @@ void showmsgv(va_list ap, const char *before1, const char *after1, const char *a argc++; } + if (argc == 1) + return; + iov[argc].iov_base = (char *)"\n"; iov[argc].iov_len = 1; + iov[0].iov_base = (char *)LOG_PREFIX; + iov[0].iov_len = sizeof(LOG_PREFIX) - 1; + + /* Try to open /dev/kmsg, log to stderr if not possible */ + fd = open(KMSG_FILENAME, O_WRONLY | O_NOCTTY | O_CLOEXEC); + if (fd < 0) + fd = 2; + writev(fd, iov, argc + 1); if (fd >= 3) diff --git a/mount.c b/mount.c index e73ab98..23934ef 100644 --- a/mount.c +++ b/mount.c @@ -25,6 +25,22 @@ #include "tiny_initramfs.h" +/* dietlibc doesn't define MS_DIRSYNC for some reason + * (probably a bug) + */ +#ifndef MS_DIRSYNC +#define MS_DIRSYNC 128 +#endif + +/* Newer mount flags (last update: 2016-01) + * (the rest are supported by both musl and dietlibc, should there be + * a C library that doesn't yet contain other flags used, feel free to + * add conditional defines here) + */ +#ifndef MS_LAZYTIME +#define MS_LAZYTIME (1 << 25) +#endif + static char supported_filesystems[MAX_SUPPORTED_FILESYSTEMS][MAX_FILESYSTEM_TYPE_LEN]; static int supported_filesystems_count; static void determine_supported_filesystems(); @@ -40,7 +56,7 @@ int mount_filesystem(const char *source, const char *target, int rc = -1; #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[----] mount_filesystem(\"", source, "\", \"", target, "\", \"", type ? type : "(null)", "\", \"", flags, "\", ...): start", NULL); + warn("[----] mount_filesystem(\"", source, "\", \"", target, "\", \"", type ? type : "(null)", "\", \"", flags, "\", ...): start", NULL); #endif if (type && (!strcmp(type, "nfs") || !strcmp(type, "nfs4"))) @@ -48,17 +64,17 @@ int mount_filesystem(const char *source, const char *target, options = parse_mount_options(data, MAX_LINE_LEN, flags, nfsver != -1 ? &nfsver : NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[----] mount_filesystem: parsing mount options (done), unparsed options: ", data, NULL); + warn("[----] mount_filesystem: parsing mount options (done), unparsed options: ", data, NULL); #endif options |= override_flags_add; options &= ~override_flags_subtract; if (type && !strcmp(type, "nfs4") && nfsver != 4) - panic(0, LOG_PREFIX, "Cannot combine [nfs]vers=2/3 option with filesystem type nfs4.", NULL); + panic(0, "Cannot combine [nfs]vers=2/3 option with filesystem type nfs4.", NULL); if (type && (!strcmp(type, "nfs") || !strcmp(type, "nfs4"))) { if (nfsver != 4 && nfsver != 0) - panic(0, LOG_PREFIX, "Sorry, only NFSv4 is currently supported.", NULL); + panic(0, "Sorry, only NFSv4 is currently supported.", NULL); /* Note that nfsver == 0 means we have type == nfs and no vers= parameter * at this point - which means that in principle we should try first NFSv4 * and then NFSv3/2. But until we support NFSv3, we'll just do NFSv4. */ @@ -66,7 +82,7 @@ int mount_filesystem(const char *source, const char *target, } #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[----] mount_filesystem: not NFS", NULL); + warn("[----] mount_filesystem: not NFS", NULL); #endif /* We need to loop through filesystem types as the kernel doesn't do @@ -78,10 +94,9 @@ int mount_filesystem(const char *source, const char *target, determine_supported_filesystems(); errno = EINVAL; - for (i = 0; i < supported_filesystems_count; i++) { + rc = -1; + for (i = 0; rc < 0 && i < supported_filesystems_count; i++) { rc = mount(source, target, supported_filesystems[i], options | MS_SILENT, data); - if (rc == 0) - return 0; } if (rc < 0) return -errno; @@ -94,91 +109,117 @@ int mount_filesystem(const char *source, const char *target, return 0; } -#define INVERTED 0x01 -#define HAS_NO_VARIANT 0x02 -#define HAS_R_VARIANT 0x04 -#define IGNORE 0x80 +/* There are precisely 4 bits currently reserved for + * kernel mount flags, so reuse them for parsing to + * save code space. */ +#define INVERTED (1U << 28) +#define HAS_NO_VARIANT (1U << 29) +#define HAS_R_VARIANT (1U << 30) +#define IGNORE (1U << 31) + +#define FLAG_MASK ~(INVERTED | HAS_NO_VARIANT | HAS_R_VARIANT | IGNORE) int parse_mount_options(char *syscall_data, size_t syscall_data_len, const char *option_string, int *nfsver) { - typedef struct { - const char *name; - int flags; - int extra; - } mount_option_t; - static mount_option_t option_definitions[] = { - { "ro", MS_RDONLY, 0 }, - { "rw", MS_RDONLY, INVERTED }, - { "exec", MS_NOEXEC, INVERTED | HAS_NO_VARIANT }, - { "suid", MS_NOSUID, INVERTED | HAS_NO_VARIANT }, - { "dev", MS_NODEV, INVERTED | HAS_NO_VARIANT }, - { "sync", MS_SYNCHRONOUS, HAS_NO_VARIANT }, -#ifdef MS_DIRSYNC - { "dirsync", MS_DIRSYNC, 0 }, -#endif - { "remount", MS_REMOUNT, 0 }, - { "bind", MS_BIND, HAS_R_VARIANT }, -#ifdef MS_NOSUB - { "sub", MS_NOSUB, INVERTED | HAS_NO_VARIANT }, -#endif -#ifdef MS_SILENT - { "silent", MS_SILENT, 0 }, - { "loud", MS_SILENT, INVERTED }, -#endif -#ifdef MS_MANDLOCK - { "mand", MS_MANDLOCK, HAS_NO_VARIANT }, -#endif - { "atime", MS_NOATIME, INVERTED | HAS_NO_VARIANT }, -#ifdef MS_I_VERSION - { "iversion", MS_I_VERSION, HAS_NO_VARIANT }, -#endif -#ifdef MS_NODIRATIME - { "diratime", MS_NODIRATIME, INVERTED | HAS_NO_VARIANT }, -#endif -#ifdef MS_RELATIME - { "relatime", MS_RELATIME, HAS_NO_VARIANT }, -#endif -#ifdef MS_STRICTATIME - { "strictatime", MS_STRICTATIME, HAS_NO_VARIANT }, -#endif - { "unbindable", MS_UNBINDABLE, HAS_R_VARIANT }, - { "private", MS_PRIVATE, HAS_R_VARIANT }, - { "slave", MS_SLAVE, HAS_R_VARIANT }, - { "shared", MS_SHARED, HAS_R_VARIANT }, - { "defaults", 0, 0 }, + /* This is not very readable, but it will save quite + * bit of space in the resulting binary... */ + static const char *mount_option_names = + /* 0 */ "ro\0" + "rw\0" + "exec\0" + "suid\0" + "dev\0" + "sync\0" + "dirsync\0" + "remount\0" + "bind\0" + "silent\0" + /* 10 */ "loud\0" + "mand\0" + "atime\0" + "iversion\0" + "diratime\0" + "relatime\0" + "strictatime\0" + "unbindable\0" + "private\0" + "slave\0" + /* 20 */ "shared\0" + "defaults\0" /* NOTE: We ignore all of these for now, but if a filesystem we * want to mount really has these options set in /etc/fstab, * it's not clear that that is the right thing to do... * (Most of them don't make sense for /usr anyway, and we * don't support loop devices.) */ - { "_netdev", 0, IGNORE }, - { "auto", 0, HAS_NO_VARIANT | IGNORE }, - { "user=", 0, HAS_NO_VARIANT | IGNORE }, - { "users", 0, HAS_NO_VARIANT | IGNORE }, - { "owner", 0, HAS_NO_VARIANT | IGNORE }, - { "group", 0, HAS_NO_VARIANT | IGNORE }, - { "comment=", 0, IGNORE }, - { "loop=", 0, IGNORE }, - { "offset=", 0, IGNORE }, - { "sizelimit=", 0, IGNORE }, - { "encryption=", 0, IGNORE }, - { "nofail", 0, IGNORE }, - { "uhelper=", 0, IGNORE }, - { "helper=", 0, IGNORE }, - { NULL, 0, 0 } + "_netdev\0" + "auto\0" + "user=\0" + "users\0" + "owner\0" + "group\0" + "comment=\0" + "loop=\0" + /* 30 */ "offset=\0" + "sizelimit=\0" + "encryption=\0" + "nofail\0" + "uhelper=\0" + "helper=\0" + ; + static const unsigned int mount_option_flags[] = { + /* 0 */ 0 | MS_RDONLY, + INVERTED | MS_RDONLY, + INVERTED | HAS_NO_VARIANT | MS_NOEXEC, + INVERTED | HAS_NO_VARIANT | MS_NOSUID, + INVERTED | HAS_NO_VARIANT | MS_NODEV, + HAS_NO_VARIANT | MS_SYNCHRONOUS, + 0 | MS_DIRSYNC, + 0 | MS_REMOUNT, + HAS_R_VARIANT | MS_BIND, + 0 | MS_SILENT, + /* 10 */ INVERTED | MS_SILENT, + HAS_NO_VARIANT | MS_MANDLOCK, + INVERTED | HAS_NO_VARIANT | MS_NOATIME, + HAS_NO_VARIANT | MS_I_VERSION, + INVERTED | HAS_NO_VARIANT | MS_NODIRATIME, + HAS_NO_VARIANT | MS_RELATIME, + HAS_NO_VARIANT | MS_STRICTATIME, + HAS_R_VARIANT | MS_UNBINDABLE, + HAS_R_VARIANT | MS_PRIVATE, + HAS_R_VARIANT | MS_SLAVE, + /* 20 */ HAS_R_VARIANT | MS_SHARED, + 0 | 0, + IGNORE | 0, + HAS_NO_VARIANT | IGNORE | 0, + HAS_NO_VARIANT | IGNORE | 0, + HAS_NO_VARIANT | IGNORE | 0, + HAS_NO_VARIANT | IGNORE | 0, + HAS_NO_VARIANT | IGNORE | 0, + IGNORE | 0, + IGNORE | 0, + /* 30 */ IGNORE | 0, + IGNORE | 0, + IGNORE | 0, + IGNORE | 0, + IGNORE | 0, + IGNORE | 0, + 0 | 0 }; - mount_option_t *optdef, *this_optdef; + char opts[MAX_LINE_LEN] = { 0 }; char *saveptr; char *token; char *check; int bits = 0; - size_t opt_name_len; int had_variant; int applies; int bits_to_change; int invert; + const char *opt_name; + size_t opt_name_len; + int opt_index, this_opt_index; + int opt_flag = 0; set_buf(opts, MAX_LINE_LEN, option_string, NULL); memset(syscall_data, 0, syscall_data_len); @@ -188,26 +229,28 @@ int parse_mount_options(char *syscall_data, size_t syscall_data_len, const char if (token[0] == 'x' && token[1] == '-') continue; - this_optdef = NULL; + this_opt_index = -1; had_variant = 0; - for (optdef = option_definitions; optdef->name; optdef++) { - opt_name_len = strlen(optdef->name); + for (opt_index = 0, opt_name = mount_option_names, opt_name_len = strlen(opt_name); + *opt_name; + ++opt_index, opt_name += opt_name_len + 1, opt_name_len = strlen(opt_name)) + { had_variant = 0; check = token; - if (optdef->extra & HAS_NO_VARIANT && strncmp(token, "no", 2) == 0) { + if (mount_option_flags[opt_index] & HAS_NO_VARIANT && strncmp(token, "no", 2) == 0) { had_variant = HAS_NO_VARIANT; check = token + 2; - } else if (optdef->extra & HAS_R_VARIANT && token[0] == 'r') { + } else if (mount_option_flags[opt_index] & HAS_R_VARIANT && token[0] == 'r') { had_variant = HAS_R_VARIANT; check = token + 1; } recheck_full: - if (optdef->name[opt_name_len - 1] == '=') { - applies = (strncmp(check, optdef->name, opt_name_len) == 0) + if (opt_name[opt_name_len - 1] == '=') { + applies = (strncmp(check, opt_name, opt_name_len) == 0) || (strlen(check) == opt_name_len - 1 && - strncmp(check, optdef->name, opt_name_len - 1) == 0); + strncmp(check, opt_name, opt_name_len - 1) == 0); } else { - applies = strcmp(check, optdef->name) == 0; + applies = strcmp(check, opt_name) == 0; } if (!applies && had_variant) { /* just in case an option starts with 'no' or 'r' */ @@ -216,20 +259,20 @@ int parse_mount_options(char *syscall_data, size_t syscall_data_len, const char goto recheck_full; } if (applies) { - this_optdef = optdef; + this_opt_index = opt_index; break; } } - if (this_optdef) { - if (this_optdef->extra & IGNORE) + if (this_opt_index != -1) { + opt_flag = mount_option_flags[this_opt_index]; + if (opt_flag & IGNORE) continue; - bits_to_change = this_optdef->flags; - if (had_variant & HAS_R_VARIANT) + bits_to_change = opt_flag & FLAG_MASK; + if (opt_flag & HAS_R_VARIANT) bits_to_change |= MS_REC; - invert = (this_optdef->extra & INVERTED); - if (had_variant & HAS_NO_VARIANT) - invert = !invert; + /* logical XOR */ + invert = !(opt_flag & INVERTED) != !(had_variant & HAS_NO_VARIANT); if (invert) bits &= ~bits_to_change; else @@ -243,12 +286,12 @@ int parse_mount_options(char *syscall_data, size_t syscall_data_len, const char char *eq = strchr(token, '=') + 1; long val; if (!*eq) - panic(0, LOG_PREFIX, "Empty NFS version specified.", NULL); + panic(0, "Empty NFS version specified.", NULL); val = strtol(eq, &endptr, 10); if (!endptr || !*endptr) - panic(0, LOG_PREFIX, "Invalid NFS version specified: ", eq, NULL); + panic(0, "Invalid NFS version specified: ", eq, NULL); if (val != 2 && val != 3 && val != 4) - panic(0, LOG_PREFIX, "Invalid NFS version specified: ", eq, NULL); + panic(0, "Invalid NFS version specified: ", eq, NULL); *nfsver = (int)val; continue; } @@ -270,7 +313,7 @@ void determine_supported_filesystems() r = traverse_file_by_line(PROC_FILESYSTEMS_FILENAME, (traverse_line_t)process_proc_filesystems, NULL); if (r < 0) - panic(-r, LOG_PREFIX, "could not determine list of kernel-supported filesystems", NULL); + panic(-r, "could not determine list of kernel-supported filesystems", NULL); } int process_proc_filesystems(void *data, const char *line, int line_is_incomplete) @@ -279,13 +322,13 @@ int process_proc_filesystems(void *data, const char *line, int line_is_incomplet /* yikes, shouldn't happen */ if (line_is_incomplete) return 0; - if (!strncmp(line, "nodev", 5) && (line[5] == ' ' || line[5] == '\t')) + if (!strncmp(line, "nodev ", 6) || !strncmp(line, "nodev\t", 6)) return 0; while (line[0] == ' ' || line[0] == '\t') ++line; if (supported_filesystems_count == MAX_SUPPORTED_FILESYSTEMS) { - warn(LOG_PREFIX, "kernel supports too many filesystem types, ignoring some " - "(please specify the rootfstype= kernel parameter if your system doesn't boot because of this)", + warn("kernel supports too many filesystem types, ignoring some " + "(please specify the rootfstype= kernel parameter if your system doesn't boot because of this)", NULL); return 0; } diff --git a/nfs.c b/nfs.c index edb51fc..2aca837 100644 --- a/nfs.c +++ b/nfs.c @@ -68,9 +68,12 @@ int mount_nfs4(const char *source, const char *target, typedef struct { const char *name; int *ptr; + } num_opt_def_t; + + typedef struct { + const char *name; int flag; - int inverse; - } opt_def_t; + } bool_opt_def_t; char p_options[MAX_LINE_LEN], *token, *saveptr, *opt_val, *endptr; long val; @@ -89,31 +92,33 @@ int mount_nfs4(const char *source, const char *target, int dummy; int had_warning; - opt_def_t num_opt_defs[] = { - { "rsize", &data.rsize, 0, 0 }, - { "wsize", &data.wsize, 0, 0 }, - { "timeo", &data.timeo, 0, 0 }, - { "retrans", &data.retrans, 0, 0 }, - { "acregmin", &data.acregmin, 0, 0 }, - { "acregmax", &data.acregmax, 0, 0 }, - { "acdirmin", &data.acdirmin, 0, 0 }, - { "acdirmax", &data.acdirmax, 0, 0 }, - { "retry", &retry, 0, 0 }, - { "vers", &dummy, 0, 0 }, - { NULL, NULL, 0, 0 } + num_opt_def_t num_opt_defs[] = { + { "rsize", &data.rsize }, + { "wsize", &data.wsize }, + { "timeo", &data.timeo }, + { "retrans", &data.retrans }, + { "acregmin", &data.acregmin }, + { "acregmax", &data.acregmax }, + { "acdirmin", &data.acdirmin }, + { "acdirmax", &data.acdirmax }, + { "retry", &retry }, + { "vers", &dummy }, + { NULL, NULL } }; - opt_def_t bool_opt_defs[] = { - { "bg", &bg, 0, 0 }, - { "fg", &bg, 0, 1 }, - { "soft", NULL, NFS4_MOUNT_SOFT, 0 }, - { "hard", NULL, NFS4_MOUNT_SOFT, 1 }, - { "intr", NULL, NFS4_MOUNT_INTR, 0 }, - { "cto", NULL, NFS4_MOUNT_NOCTO, 1 }, - { "ac", NULL, NFS4_MOUNT_NOAC, 1 }, - { "sharedcache", NULL, NFS4_MOUNT_UNSHARED, 1 }, - { NULL, NULL, 0, 0 } +#define INVERTED 0x10000 + bool_opt_def_t bool_opt_defs[] = { + { "bg", 0 }, + { "fg", INVERTED }, + { "soft", NFS4_MOUNT_SOFT }, + { "hard", NFS4_MOUNT_SOFT | INVERTED }, + { "intr", NFS4_MOUNT_INTR }, + { "cto", NFS4_MOUNT_NOCTO | INVERTED }, + { "ac", NFS4_MOUNT_NOAC | INVERTED }, + { "sharedcache", NFS4_MOUNT_UNSHARED | INVERTED }, + { NULL, 0 } }; - opt_def_t *opt_def; + num_opt_def_t *num_opt_def; + bool_opt_def_t *bool_opt_def; set_buf(p_options, MAX_LINE_LEN, nfs_options, NULL); @@ -126,14 +131,14 @@ int mount_nfs4(const char *source, const char *target, opt_val = strchr((char *)source, ':'); if (!opt_val) - panic(0, LOG_PREFIX, "nfs mount: directory to mount not in host:dir format: ", source, NULL); + panic(0, "nfs mount: directory to mount not in host:dir format: ", source, NULL); strncpy(hostname, source, MIN(MAX_LINE_LEN - 1, opt_val - source)); set_buf(mnt_path, MAX_LINE_LEN, opt_val + 1, NULL); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(NFS_PORT); if (!small_inet_aton(hostname, &server_addr.sin_addr)) - panic(0, LOG_PREFIX, "nfs mount: only IP addresses supported for mounting NFS servers, got ", hostname, " instead.", NULL); + panic(0, "nfs mount: only IP addresses supported for mounting NFS servers, got ", hostname, " instead.", NULL); for (token = strtok_r(p_options, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) { opt_val = strchr(token, '='); @@ -146,24 +151,24 @@ int mount_nfs4(const char *source, const char *target, else if (strcmp(opt_val, "udp") == 0) data.proto = IPPROTO_UDP; else - panic(0, LOG_PREFIX, "nfs mount: invalid proto option specified (valid values are: tcp, udp)", NULL); + panic(0, "nfs mount: invalid proto option specified (valid values are: tcp, udp)", NULL); continue; } else if (strcmp(token, "clientaddr") == 0) { /* FIXME */ - panic(0, LOG_PREFIX, "nfs mount: clientaddr not supported yet", NULL); + panic(0, "nfs mount: clientaddr not supported yet", NULL); } else if (strcmp(token, "sec") == 0) { if (strcmp(opt_val + 1, "sys") != 0) - panic(0, LOG_PREFIX, "nfs mount: only sec=sys is supported", NULL); + panic(0, "nfs mount: only sec=sys is supported", NULL); continue; } if (!*opt_val) - panic(0, LOG_PREFIX, "nfs mount: invalid empty option ", token, " specified", NULL); + panic(0, "nfs mount: invalid empty option ", token, " specified", NULL); endptr = NULL; val = strtol(opt_val, &endptr, 10); if (!endptr || !*endptr) - panic(0, LOG_PREFIX, "nfs mount: option ", token, " requires a number, got ", opt_val, " instead.", NULL); + panic(0, "nfs mount: option ", token, " requires a number, got ", opt_val, " instead.", NULL); if (strcmp(token, "port") == 0) { server_addr.sin_port = htons((int)val); @@ -175,14 +180,14 @@ int mount_nfs4(const char *source, const char *target, continue; } - for (opt_def = num_opt_defs; opt_def->name; opt_def++) { - if (strcmp(token, opt_def->name) == 0) { - *opt_def->ptr = (int)val; + for (num_opt_def = num_opt_defs; num_opt_def->name; num_opt_def++) { + if (strcmp(token, num_opt_def->name) == 0) { + *num_opt_def->ptr = (int)val; break; } } - if (!opt_def->name) - panic(0, LOG_PREFIX, "nfs mount: invalid option ", token, "=", opt_val, NULL); + if (!num_opt_def->name) + panic(0, "nfs mount: invalid option ", token, "=", opt_val, NULL); } else { val = 1; if (strncmp(token, "no", 2) == 0) { @@ -191,31 +196,30 @@ int mount_nfs4(const char *source, const char *target, } else { opt_val = token; } - for (opt_def = bool_opt_defs; opt_def->name; opt_def++) { - if (strcmp(opt_val, opt_def->name) == 0) { - if (opt_def->ptr) { - if (opt_def->inverse) - *opt_def->ptr = !(int)val; - else - *opt_def->ptr = (int)val; - } else { - if (opt_def->inverse) - val = !val; + if (strcmp(opt_val, "bg") == 0) { + bg = 1; + } else if (strcmp(opt_val, "fg") == 0) { + bg = 0; + } else { + for (bool_opt_def = bool_opt_defs; bool_opt_def->name; bool_opt_def++) { + if (strcmp(opt_val, bool_opt_def->name) == 0) { + /* != is logical XOR in C */ + val = val != !!(bool_opt_def->flag & INVERTED); if (val) - data.flags |= opt_def->flag; + data.flags |= (bool_opt_def->flag & NFS4_MOUNT_FLAGMASK); else - data.flags &= ~opt_def->flag; + data.flags &= ~(bool_opt_def->flag & NFS4_MOUNT_FLAGMASK); + break; } - break; } + if (!bool_opt_def->name) + panic(0, "nfs mount: invalid option ", token, NULL); } - if (!opt_def->name) - panic(0, LOG_PREFIX, "nfs mount: invalid option ", token, NULL); } } if (bg) { - warn(LOG_PREFIX, "nfs mount: background mounts unsupported for / and /usr, defaulting to foreground", NULL); + warn("nfs mount: background mounts unsupported for / and /usr, defaulting to foreground", NULL); bg = 0; } @@ -246,17 +250,16 @@ int mount_nfs4(const char *source, const char *target, if (time(NULL) >= timeout) { if (r < 0 && r != -ETIMEDOUT) - panic(r, LOG_PREFIX, "nfs mount: failed to mount ", source, NULL); + panic(r, "nfs mount: failed to mount ", source, NULL); else - panic(0, LOG_PREFIX, "nfs mount: timeout while trying to mount ", source, NULL); + panic(0, "nfs mount: timeout while trying to mount ", source, NULL); } if (!had_warning) { had_warning = 1; - if (r < 0) - warn(LOG_PREFIX, "nfs mount: waiting for response from NFS server ", hostname, ": ", strerror(-r), NULL); - else - warn(LOG_PREFIX, "nfs mount: waiting for response from NFS server ", hostname, ": timeout", NULL); + if (r >= 0) + r = -ETIMEDOUT; + warn("nfs mount: waiting for response from NFS server ", hostname, ": ", strerror(-r), NULL); } /* Wait a bit before retrying, otherwise we will flood the network... */ @@ -321,6 +324,7 @@ int nfs4_ping(int domain, int type, struct sockaddr *dest, socklen_t dest_len, i struct pollfd poll_fd; int timeout_msec = timeout * 1000; int pos = 0; + size_t msg_start; socklen_t len; union { @@ -363,7 +367,9 @@ int nfs4_ping(int domain, int type, struct sockaddr *dest, socklen_t dest_len, i if (type == SOCK_DGRAM) { state = WAIT_FOR_SEND; + msg_start = 4; } else { + msg_start = 0; r = connect(sock_fd, dest, dest_len); if (r < 0 && errno != EINPROGRESS && errno != EWOULDBLOCK) goto error_out; @@ -388,6 +394,7 @@ int nfs4_ping(int domain, int type, struct sockaddr *dest, socklen_t dest_len, i state = WAIT_FOR_SEND; break; case WAIT_FOR_SEND: + /* UDP doesn't have fragment length */ if (type == SOCK_DGRAM) bytes = sendto(sock_fd, nullproc_request + 4, sizeof(nullproc_request) - 4, 0, dest, dest_len); else @@ -402,8 +409,7 @@ int nfs4_ping(int domain, int type, struct sockaddr *dest, socklen_t dest_len, i break; case WAIT_FOR_RECEIVE: if (type == SOCK_DGRAM) { - /* Fragment length only available over TCP, so we fake it for UDP */ - memcpy(nullproc_response, &nullproc_expected_response, 4); + /* UDP doesn't have fragment length */ bytes = recvfrom(sock_fd, nullproc_response + 4, sizeof(nullproc_response) - 4, 0, dest, &dest_len); if (bytes != (int)sizeof(nullproc_response) - 4) { if (bytes >= 0) @@ -442,10 +448,10 @@ int nfs4_ping(int domain, int type, struct sockaddr *dest, socklen_t dest_len, i return r; /* Compare the response to the expected response */ - r = memcmp(&nullproc_expected_response, &nullproc_response, sizeof(nullproc_response)); + r = memcmp(&nullproc_expected_response[msg_start], &nullproc_response[msg_start], sizeof(nullproc_response) - msg_start); if (r == 0 && ip_addr) { /* Write string representation of client address to ip_addr */ - memset(ip_addr, 0, ip_addr_len - 1); + *ip_addr = '\0'; if (domain == AF_INET) set_buf(ip_addr, ip_addr_len, small_inet_ntoa(client_addr.in.sin_addr), NULL); } @@ -482,9 +488,7 @@ int small_inet_aton(const char *cp, struct in_addr *inp) for (i = 0, ptr = (char *)cp; i < 4; i++, ptr = endptr + 1) { endptr = NULL; value = strtoul(ptr, &endptr, 10); - if (!endptr || endptr == ptr || *endptr != ip_part_terminator_chars[i]) - return 0; - if (value >= 256) + if (value >= 256 || !endptr || endptr == ptr || *endptr != ip_part_terminator_chars[i]) return 0; result.bytes[i] = (char)value; } diff --git a/tiny_initramfs.c b/tiny_initramfs.c index a79d8eb..328c965 100644 --- a/tiny_initramfs.c +++ b/tiny_initramfs.c @@ -54,65 +54,76 @@ int main(int argc, char **argv) int timeout_togo = DEVICE_TIMEOUT; fstab_info usrfs_info; char real_device_name[MAX_PATH_LEN] = { 0 }; + size_t root_fstype_len, root_nfsdir_len, root_nfsoptions_len, root_options_len; #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 0 ] Startup", NULL); + warn("[ 0 ] Startup", NULL); #endif r = mount("proc", "/proc", "proc", MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL); if (r < 0) - panic(errno, LOG_PREFIX, "Could not mount /proc", NULL); + panic(errno, "Could not mount /proc", NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 1 ] /proc mounted", NULL); + warn("[ 1 ] /proc mounted", NULL); #endif r = mount("udev", "/dev", "devtmpfs", 0, DEVTMPFS_MOUNTOPTS); if (r < 0) - panic(errno, LOG_PREFIX, "Could not mount /dev (as devtmpfs)", NULL); + panic(errno, "Could not mount /dev (as devtmpfs)", NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 2 ] /dev mounted", NULL); + warn("[ 2 ] /dev mounted", NULL); #endif parse_cmdline(); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 3 ] ", PROC_CMDLINE_FILENAME, " parsed", NULL); + warn("[ 3 ] ", PROC_CMDLINE_FILENAME, " parsed", NULL); #endif - if (!strlen(root_device)) - panic(0, LOG_PREFIX, "No root filesystem (root=) specified", NULL); + if (!strlen(root_device)) { + if (strlen(root_nfshost)) + set_buf(root_device, MAX_PATH_LEN, "/dev/nfs", NULL); + else + panic(0, "No root filesystem (root=) specified", NULL); + } + + root_fstype_len = strlen(root_fstype); if (strcmp(root_device, "/dev/nfs") == 0) { /* We have nfsroot, so build together new device name */ if (!strlen(root_nfshost)) { r = find_bootserver_from_pnp(); if (r < 0 || !strlen(root_nfshost)) - panic(r ? -r : ENOENT, LOG_PREFIX, "Failed to determine boot server from kernel", NULL); + panic(r ? -r : ENOENT, "Failed to determine boot server from kernel", NULL); } /* Make sure file system type is set properly */ - if (!strlen(root_fstype) || (strcmp(root_fstype, "nfs") != 0 && strcmp(root_fstype, "nfs4") != 0)) { - if (strlen(root_fstype)) - warn(LOG_PREFIX, "rootfstype set to ", root_fstype, " but root=/dev/nfs specified. Assuming rootfstype=nfs.", NULL); + if (!root_fstype_len || (strcmp(root_fstype, "nfs") != 0 && strcmp(root_fstype, "nfs4") != 0)) { + if (root_fstype_len) + warn("rootfstype set to ", root_fstype, " but root=/dev/nfs specified. Assuming rootfstype=nfs.", NULL); set_buf(root_fstype, MAX_FILESYSTEM_TYPE_LEN, "nfs", NULL); } + root_nfsdir_len = strlen(root_nfsdir); + root_nfsoptions_len = strlen(root_nfsoptions); + root_options_len = strlen(root_options); + /* This will be special-cased when mounting the filesystem to * replace it with the IP. */ - if (!strlen(root_nfsdir)) + if (!root_nfsdir_len) set_buf(root_nfsdir, MAX_LINE_LEN, DEFAULT_ROOTFS_NFS_DIR, NULL); - if (strlen(root_nfshost) + 1 + strlen(root_nfsdir) + 1 > MAX_PATH_LEN) - panic(0, LOG_PREFIX, "nfsroot=", root_nfshost, ":", root_nfsdir, " too long.", NULL); + if (strlen(root_nfshost) + 1 + root_nfsdir_len + 1 > MAX_PATH_LEN) + panic(0, "nfsroot=", root_nfshost, ":", root_nfsdir, " too long.", NULL); set_buf(real_device_name, MAX_PATH_LEN, root_nfshost, ":", root_nfsdir, NULL); - if (strlen(root_nfsoptions)) { - if (strlen(root_options) + strlen(root_nfsoptions) + 2 > MAX_LINE_LEN) - panic(0, LOG_PREFIX, "nfsroot options (\"", root_nfsoptions, "\") too long.", NULL); - append_to_buf(root_options, MAX_LINE_LEN, strlen(root_options) ? "," : "", root_nfsoptions, NULL); + if (root_nfsoptions_len) { + if (root_options_len + root_nfsoptions_len + 2 > MAX_LINE_LEN) + panic(0, "nfsroot options (\"", root_nfsoptions, "\") too long.", NULL); + append_to_buf(root_options, MAX_LINE_LEN, root_options_len ? "," : "", root_nfsoptions, NULL); } } else { if (root_wait_indefinitely) @@ -121,34 +132,34 @@ int main(int argc, char **argv) } #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 4 ] waited for root device", NULL); + warn("[ 4 ] waited for root device", NULL); #endif r = mount_filesystem(real_device_name, TARGET_DIRECTORY, strlen(root_fstype) ? root_fstype : NULL, root_options, global_rw ? 0 : MS_RDONLY, global_rw ? MS_RDONLY : 0); if (r < 0) - panic(-r, LOG_PREFIX, "Failed to mount root filesystem from ", root_device, NULL); + panic(-r, "Failed to mount root filesystem from ", root_device, NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 5 ] mounted root filesystem", NULL); + warn("[ 5 ] mounted root filesystem", NULL); #endif /* We need these regardless of /usr handling */ if (access(TARGET_DIRECTORY "/dev", F_OK) != 0) - panic(errno, LOG_PREFIX, "/dev doesn't exist on root filesystem", NULL); + panic(errno, "/dev doesn't exist on root filesystem", NULL); if (access(TARGET_DIRECTORY "/proc", F_OK) != 0) - panic(errno, LOG_PREFIX, "/proc doesn't exist on root filesystem", NULL); + panic(errno, "/proc doesn't exist on root filesystem", NULL); /* Make sure we mount /usr if present in /etc/fstab * (no /etc/fstab is no error, we just assume that there'll * be no entry then) */ r = fstab_find_fs("/usr", &usrfs_info); if (r < 0 && r != -ENOENT && r != -ENODEV) - panic(-r, LOG_PREFIX, "Failed to parse /etc/fstab in root device (non-existence would not be an error)", NULL); + panic(-r, "Failed to parse /etc/fstab in root device (non-existence would not be an error)", NULL); if (r == -ENODEV) - panic(0, LOG_PREFIX, "Entry in /etc/fstab for /usr must be a (non-symlink) kernel device path, or of the form UUID=, or an NFS filesystem..", NULL); + panic(0, "Entry in /etc/fstab for /usr must be a (non-symlink) kernel device path, or of the form UUID=, or an NFS filesystem..", NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 6 ] parsed /etc/fstab", NULL); + warn("[ 6 ] parsed /etc/fstab", NULL); #endif if (r == 0) { @@ -159,7 +170,7 @@ int main(int argc, char **argv) wait_for_device(real_device_name, &timeout_togo, usrfs_info.source, 0); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 6.1] waited for /usr device", NULL); + warn("[ 6.1] waited for /usr device", NULL); #endif } else { set_buf(real_device_name, MAX_PATH_LEN, usrfs_info.source, NULL); @@ -170,17 +181,17 @@ int main(int argc, char **argv) usr_rw_override = 0; #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 6.1] no need to wait for /usr device (NFS)", NULL); + warn("[ 6.1] no need to wait for /usr device (NFS)", NULL); #endif } /* mount it */ r = mount_filesystem(real_device_name, TARGET_DIRECTORY "/usr", usrfs_info.type, usrfs_info.options, usr_rw_override ? 0 : MS_RDONLY, usr_rw_override ? MS_RDONLY : 0); if (r < 0) - panic(-r, LOG_PREFIX, "Failed to mount /usr filesystem from ", usrfs_info.source, NULL); + panic(-r, "Failed to mount /usr filesystem from ", usrfs_info.source, NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 6.2] mounted /usr filesystem", NULL); + warn("[ 6.2] mounted /usr filesystem", NULL); #endif } @@ -188,18 +199,18 @@ int main(int argc, char **argv) r = mount("/dev", TARGET_DIRECTORY "/dev", NULL, MS_MOVE, NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 7 ] moved /dev", NULL); + warn("[ 7 ] moved /dev", NULL); #endif if (!r) r = mount("/proc", TARGET_DIRECTORY "/proc", NULL, MS_MOVE, NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 8 ] moved /proc", NULL); + warn("[ 8 ] moved /proc", NULL); #endif if (r < 0) - panic(errno, LOG_PREFIX, "Couldn't move /dev or /proc from initramfs to root filesystem", NULL); + panic(errno, "Couldn't move /dev or /proc from initramfs to root filesystem", NULL); /* switch root */ r = chdir(TARGET_DIRECTORY); @@ -208,14 +219,14 @@ int main(int argc, char **argv) if (!r) r = chroot("."); if (r < 0) - panic(errno, LOG_PREFIX, "Couldn't switch root filesystem", NULL); + panic(errno, "Couldn't switch root filesystem", NULL); #ifdef DEBUG_INITRAMFS - warn(LOG_PREFIX, "[ 9 ] switched root, output of /proc/self/mountinfo now is:", NULL); + warn("[ 9 ] switched root, output of /proc/self/mountinfo now is:", NULL); debug_dump_file("/proc/self/mountinfo"); - warn(LOG_PREFIX, "[10 ] sleeping for 5s", NULL); + warn("[10 ] sleeping for 5s", NULL); sleep(5); - warn(LOG_PREFIX, "[11 ] booting the system", NULL); + warn("[11 ] booting the system", NULL); #endif if (strlen(init_binary)) { @@ -230,6 +241,7 @@ int main(int argc, char **argv) /* Message stolen from Linux's init/main.c */ panic(0, "No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance.", NULL); + _exit(1); return 1; } @@ -238,7 +250,7 @@ void parse_cmdline() int r; r = traverse_file_by_line(PROC_CMDLINE_FILENAME, (traverse_line_t)parse_cmdline_helper, NULL); if (r < 0) - panic(-r, LOG_PREFIX, "Could not parse ", PROC_CMDLINE_FILENAME, NULL); + panic(-r, "Could not parse ", PROC_CMDLINE_FILENAME, NULL); } int parse_cmdline_helper(void *data, const char *line, int line_is_incomplete) @@ -257,9 +269,9 @@ int parse_cmdline_helper(void *data, const char *line, int line_is_incomplete) if (!strncmp(token, "root=", 5)) { token += 5; if (strlen(token) > MAX_PATH_LEN - 1) - panic(0, LOG_PREFIX, "Parameter root=", token, " too long", NULL); + panic(0, "Parameter root=", token, " too long", NULL); if (!is_valid_device_name(token, NULL, NULL, NULL, NULL)) - panic(0, LOG_PREFIX, "Parameter root=", token, " unsupported (only /dev/, 0xMAJMIN and UUID= are supported)", NULL); + panic(0, "Parameter root=", token, " unsupported (only /dev/, 0xMAJMIN and UUID= are supported)", NULL); set_buf(root_device, MAX_PATH_LEN, token, NULL); } else if (!strncmp(token, "rootflags=", 10)) { token += 10; @@ -269,13 +281,13 @@ int parse_cmdline_helper(void *data, const char *line, int line_is_incomplete) } else if (!strncmp(token, "rootfstype=", 11)) { token += 11; if (strlen(token) > MAX_FILESYSTEM_TYPE_LEN - 1) - panic(0, LOG_PREFIX, "Parameter rootfstype=", token, " too long", NULL); + panic(0, "Parameter rootfstype=", token, " too long", NULL); set_buf(root_fstype, MAX_FILESYSTEM_TYPE_LEN, token, NULL); } else if (!strncmp(token, "rootdelay=", 10)) { token += 10; lval = strtoul(token, &endptr, 10); if (!*token || !endptr || *endptr || lval > INT_MAX) - panic(0, LOG_PREFIX, "Invalid rootdelay=", token," value, must be integer (and must fit into integer data type)", NULL); + panic(0, "Invalid rootdelay=", token," value, must be integer (and must fit into integer data type)", NULL); root_delay = (int) lval; } else if (!strcmp(token, "rootwait")) { root_wait_indefinitely = 1; @@ -286,7 +298,7 @@ int parse_cmdline_helper(void *data, const char *line, int line_is_incomplete) } else if (!strncmp(token, "init=", 5)) { token += 5; if (strlen(token) > MAX_PATH_LEN - 1) - panic(0, LOG_PREFIX, "Parameter init=", token, " too long", NULL); + panic(0, "Parameter init=", token, " too long", NULL); set_buf(init_binary, MAX_PATH_LEN, token, NULL); } else if (!strncmp(token, "nfsroot=", 8)) { char *ptr;