diff options
Diffstat (limited to 'util/nvmutil/nvmutil.c')
| -rw-r--r-- | util/nvmutil/nvmutil.c | 1179 |
1 files changed, 637 insertions, 542 deletions
diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index f6374a7a..ecc003ac 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -42,9 +42,26 @@ * is passed. The rest of the program * will manipulate this data. */ +/* +TODO: +eventually, i will not have this return +a pointer at all. instead, a similar key +mechanism will be used for other access +functions e.g. word/set_word, err(needs +to clean up), and so on. then those +would return values if already initialised, +but would not permit additional init - will +decide exactly how to implement this at a +later state. + +this is part of an ongoing effort to introduce +extreme memory safety into this program. + */ struct xstate * -new_xstate(void) +xstatus(void) { + static int first_run = 1; + static struct xstate us = { /* .cmd (update cmd[] in the struct if adding to it) DO NOT FORGET. or C will init zeroes/NULLs */ @@ -79,12 +96,12 @@ new_xstate(void) CHECKSUM_READ, SKIP_CHECKSUM_WRITE, GBE_PART_SIZE, O_RDONLY }, { - CMD_CAT16, "cat16", cmd_helper_cat, ARGC_3, + CMD_CAT16, "cat16", cmd_helper_cat16, ARGC_3, ARG_NOPART, CHECKSUM_READ, SKIP_CHECKSUM_WRITE, GBE_PART_SIZE, O_RDONLY }, { - CMD_CAT128, "cat128", cmd_helper_cat, ARGC_3, + CMD_CAT128, "cat128", cmd_helper_cat128, ARGC_3, ARG_NOPART, CHECKSUM_READ, SKIP_CHECKSUM_WRITE, GBE_PART_SIZE, O_RDONLY @@ -94,7 +111,7 @@ new_xstate(void) /* ->mac */ {NULL, "xx:xx:xx:xx:xx:xx", {0, 0, 0}}, /* .str, .rmac, .mac_buf */ - /* .buf */ + /* .f */ {0}, /* .argv0 (for our getprogname implementation) */ @@ -107,67 +124,58 @@ new_xstate(void) 1, /* .xsize (size of the stuct will be stored here later) */ - 0 - - }; - - struct xstate *xs_new; + 0, - us.xsize = sizeof(us); + /* .cat (cat helpers set this) */ + -1 - xs_new = malloc(us.xsize); - if (xs_new == NULL) - err(ECANCELED, "Could not initialise new state"); + }; - memcpy(xs_new, &us, us.xsize); + if (first_run) { + us.xsize = sizeof(us); - /* - * Some pointers should be set - * in the copy, not the template, - * if they point to other items - * in the struct, because if they - * were set before copy, then copied, - * the copied pointer would be invalid, - * referring to the reference struct - * - * e.g. ->f.buf (gbe tmp file work memory): - */ + us.f.buf = us.f.real_buf; - xs_new->f.buf = xs_new->f.real_buf; + us.f.gbe_fd = -1; + us.f.tmp_fd = -1; - xs_new->f.gbe_fd = -1; - xs_new->f.tmp_fd = -1; + us.f.tname = NULL; + us.f.fname = NULL; + } - xs_new->f.tname = NULL; - xs_new->f.fname = NULL; + first_run = 0; - return xs_new; + return &us; } -static struct xstate *nv = NULL; - int main(int argc, char *argv[]) { + struct xstate *x; struct commands *cmd; struct xfile *f; + unsigned long i; - unsigned long *i; + char *tmp_path; - nv = new_xstate(); - if (nv == NULL) - err(errno, NULL); + struct stat st; + int fd; - nv->argv0 = argv[0]; - if (argc < 3) - usage(); + tmp_path = NULL; +#ifndef S_ISREG + err(ECANCELED, "Can't determine file types (S_ISREG undefined)"); +#endif + +#ifndef CHAR_BIT + err(ECANCELED, "Unknown char size"); +#else if (CHAR_BIT != 8) err(EINVAL, "Unsupported char size"); +#endif - f = &nv->f; - - f->fname = argv[1]; + if (argc < 3) + usage(); #ifdef NVMUTIL_UNVEIL /* @@ -175,47 +183,65 @@ main(int argc, char *argv[]) * unveil would trap on final file rename * and we can't know the path in advance */ - f->tname = new_tmpfile(&f->tmp_fd, 1, NULL); + tmp_path = new_tmpfile(&fd, 1, NULL); #else - f->tname = new_tmpfile(&f->tmp_fd, 0, NULL); + tmp_path = new_tmpfile(&fd, 0, NULL); #endif - if (f->tname == NULL) + if (tmp_path == NULL) err(errno, "Can't create tmpfile"); #ifdef NVMUTIL_PLEDGE #ifdef NVMUTIL_UNVEIL if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1) err(errno, "pledge, unveil"); - if (unveil("/dev/urandom", "r") == -1) - err(errno, "unveil: /dev/urandom"); - if (unveil("/dev/random", "r") == -1) - err(errno, "unveil: /dev/random"); + if (unveil("/dev/null", "r") == -1) + err(errno, "unveil: /dev/null"); #else if (pledge("stdio flock rpath wpath cpath", NULL) == -1) err(errno, "pledge"); #endif #endif + x = xstatus(); + + if (x == NULL) + err(errno, NULL); + if (x->f.buf == NULL) + err(EINVAL, "Work buffer not initialised"); + + x->argv0 = argv[0]; + f = &x->f; + + f->fname = argv[1]; + f->tname = tmp_path; + f->tmp_fd = fd; + + if(fstat(fd, &st) < 0) + err(errno, "can't stat tmpfile"); + + f->tmp_dev = st.st_dev; + f->tmp_ino = st.st_ino; + sanitize_command_list(); set_cmd(argc, argv); set_cmd_args(argc, argv); - i = &nv->i; - cmd = &nv->cmd[*i]; + i = x->i; + cmd = &x->cmd[i]; #ifdef NVMUTIL_UNVEIL - if (cmd->flags == O_RDONLY) { - if (unveil(fname, "r") == -1) - err(errno, "%s: unveil r", fname); + if ((cmd->flags & O_ACCMODE) == O_RDONLY) { + if (unveil(f->fname, "r") == -1) + err(errno, "%s: unveil r", f->fname); } else { - if (unveil(fname, "rwc") == -1) - err(errno, "%s: unveil rw", fname); + if (unveil(f->tname, "rwc") == -1) + err(errno, "%s: unveil rw", f->tname); } - if (unveil(tname, "rwc") == -1) - err(errno, "%s: unveil rwc", tname); + if (unveil(f->tname, "rwc") == -1) + err(errno, "%s: unveil rwc", f->tname); if (unveil(NULL, NULL) == -1) err(errno, "unveil block (rw)"); @@ -224,8 +250,6 @@ main(int argc, char *argv[]) err(errno, "pledge (kill unveil)"); #endif - srand((unsigned int)(time(NULL) ^ getpid())); - open_gbe_file(); memset(f->buf, 0, GBE_BUF_SIZE); @@ -237,7 +261,7 @@ main(int argc, char *argv[]) run_cmd(); - if (cmd->flags == O_RDWR) + if ((cmd->flags & O_ACCMODE) == O_RDWR) write_to_gbe_bin(); if (exit_cleanup() == -1) @@ -258,8 +282,12 @@ main(int argc, char *argv[]) void sanitize_command_list(void) { + struct xstate *x = xstatus(); + unsigned long c; - unsigned long num_commands = items(nv->cmd); + unsigned long num_commands; + + num_commands = items(x->cmd); for (c = 0; c < num_commands; c++) sanitize_command_index(c); @@ -271,9 +299,13 @@ sanitize_command_list(void) void sanitize_command_index(unsigned long c) { + struct xstate *x = xstatus(); + struct commands *cmd; + + int _flag; unsigned long gbe_rw_size; - struct commands *cmd = &nv->cmd[c]; + cmd = &x->cmd[c]; check_command_num(c); @@ -318,30 +350,33 @@ sanitize_command_index(unsigned long c) err(EINVAL, "rw_size larger than GbE part: %lu", (unsigned long)gbe_rw_size); - if (cmd->flags != O_RDONLY && - cmd->flags != O_RDWR) + _flag = (cmd->flags & O_ACCMODE); + + if (_flag != O_RDONLY && + _flag != O_RDWR) err(EINVAL, "invalid cmd.flags setting"); } void set_cmd(int argc, char *argv[]) { + struct xstate *x = xstatus(); const char *cmd; - unsigned long i = 0; + unsigned long c; - for (i = 0; i < items(nv->cmd); i++) { + for (c = 0; c < items(x->cmd); c++) { - cmd = nv->cmd[i].str; + cmd = x->cmd[c].str; /* not the right command */ if (xstrxcmp(argv[2], cmd, MAX_CMD_LEN) != 0) continue; /* valid command found */ - if (argc >= nv->cmd[i].argc) { - nv->no_cmd = 0; - nv->i = i; /* set command */ + if (argc >= x->cmd[c].argc) { + x->no_cmd = 0; + x->i = c; /* set command */ return; } @@ -350,48 +385,44 @@ set_cmd(int argc, char *argv[]) "Too few args on command '%s'", cmd); } - nv->no_cmd = 1; + x->no_cmd = 1; } void set_cmd_args(int argc, char *argv[]) { - unsigned char arg_part; + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; unsigned long i; - struct xfile *f; - struct commands *cmd; + i = x->i; + cmd = &x->cmd[i]; + f = &x->f; - if (!valid_command(nv->i) || argc < 3) + if (!valid_command(i) || argc < 3) usage(); - if (nv->no_cmd) + if (x->no_cmd) usage(); - i = nv->i; - f = &nv->f; - - cmd = &nv->cmd[i]; - - arg_part = cmd->arg_part; - /* Maintainer bugs */ - if (arg_part && argc < 4) + if (cmd->arg_part && argc < 4) err(EINVAL, "arg_part set for command that needs argc4"); - if (arg_part && i == CMD_SETMAC) + if (cmd->arg_part && i == CMD_SETMAC) err(EINVAL, "arg_part set on CMD_SETMAC"); if (i == CMD_SETMAC) { if (argc >= 4) - nv->mac.str = argv[3]; + x->mac.str = argv[3]; else - nv->mac.str = nv->mac.rmac; + x->mac.str = x->mac.rmac; - } else if (arg_part) { + } else if (cmd->arg_part) { f->part = conv_argv_part_num(argv[3]); } @@ -429,6 +460,7 @@ xstrxcmp(const char *a, const char *b, unsigned long maxlen) err(EINVAL, "Empty string in xstrxcmp"); for (i = 0; i < maxlen; i++) { + unsigned char ac = (unsigned char)a[i]; unsigned char bc = (unsigned char)b[i]; @@ -457,14 +489,15 @@ xstrxcmp(const char *a, const char *b, unsigned long maxlen) void open_gbe_file(void) { + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; + struct stat _st; int _flags; - unsigned long *i = &nv->i; - - struct commands *cmd = &nv->cmd[*i]; - - struct xfile *f = &nv->f; + cmd = &x->cmd[x->i]; + f = &x->f; xopen(&f->gbe_fd, f->fname, cmd->flags | O_BINARY | @@ -506,19 +539,18 @@ open_gbe_file(void) err(EINVAL, "File size must be 8KB, 16KB or 128KB"); } - if (lock_file(f->gbe_fd) == -1) + if (lock_file(f->gbe_fd, cmd->flags) == -1) err(errno, "%s: can't lock", f->fname); } int -lock_file(int fd) +lock_file(int fd, int flags) { struct flock fl; - struct commands *cmd = &nv->cmd[nv->i]; memset(&fl, 0, sizeof(fl)); - if (cmd->flags == O_RDONLY) + if ((flags & O_ACCMODE) == O_RDONLY) fl.l_type = F_RDLCK; else fl.l_type = F_WRLCK; @@ -531,6 +563,21 @@ lock_file(int fd) return 0; } +/* + * TODO: make generic. S_ISREG: check every other + * type, erring only if it doesn't match what was + * passed as type requested. + * also: + * have variable need_seek, only err on seek if + * need_seek is set. + * also consider the stat check in this generic + * context + * make tthe return type an int, not a void. + * return -1 with errno set to indicate error, + * though the syscalls mostly handle that. + * save errno before lseek, resetting it after + * the check if return >-1 + */ void xopen(int *fd_ptr, const char *path, int flags, struct stat *st) { @@ -561,7 +608,10 @@ xopen(int *fd_ptr, const char *path, int flags, struct stat *st) void copy_gbe(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + f = &x->f; read_file(); @@ -586,10 +636,13 @@ copy_gbe(void) void read_file(void) { - long _r; + struct xstate *x = xstatus(); + struct xfile *f; + struct stat _st; + long _r; - struct xfile *f = &nv->f; + f = &x->f; /* read main file */ _r = rw_file_exact(f->gbe_fd, f->buf, f->gbe_file_size, @@ -644,13 +697,18 @@ read_file(void) void read_checksums(void) { + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; + unsigned long _p; unsigned long _skip_part; + unsigned char _num_invalid; unsigned char _max_invalid; - struct xfile *f = &nv->f; - struct commands *cmd = &nv->cmd[nv->i]; + cmd = &x->cmd[x->i]; + f = &x->f; f->part_valid[0] = 0; f->part_valid[1] = 0; @@ -694,30 +752,41 @@ read_checksums(void) int good_checksum(unsigned long partnum) { - unsigned short expected_checksum = + unsigned short expected_checksum; + unsigned short actual_checksum; + + expected_checksum = calculated_checksum(partnum); - unsigned short current_checksum = + actual_checksum = nvm_word(NVM_CHECKSUM_WORD, partnum); - return current_checksum == expected_checksum; + if (expected_checksum == actual_checksum) { + return 1; + } else { + return 0; + } } void run_cmd(void) { - unsigned long cmd_num; - struct commands *cmd; + struct xstate *x = xstatus(); + unsigned long i; + void (*run)(void); - cmd_num = nv->i; - cmd = &nv->cmd[cmd_num]; + i = x->i; + run = x->cmd[i].run; - check_command_num(cmd_num); + check_command_num(i); - if (cmd->run == NULL) - err(EINVAL, "Command %lu: null ptr", cmd_num); + if (run == NULL) + err(EINVAL, "Command %lu: null ptr", i); - cmd->run(); + run(); + + for (i = 0; i < items(x->cmd); i++) + x->cmd[i].run = cmd_helper_err; } void @@ -731,12 +800,13 @@ check_command_num(unsigned long c) unsigned char valid_command(unsigned long c) { + struct xstate *x = xstatus(); struct commands *cmd; - if (c >= items(nv->cmd)) + if (c >= items(x->cmd)) return 0; - cmd = &nv->cmd[c]; + cmd = &x->cmd[c]; if (c != cmd->chk) err(EINVAL, @@ -749,8 +819,13 @@ valid_command(unsigned long c) void cmd_helper_setmac(void) { + struct xstate *x = xstatus(); unsigned long partnum; - struct macaddr *mac = &nv->mac; + struct macaddr *mac; + + mac = &x->mac; + + check_cmd(cmd_helper_setmac, "setmac"); printf("MAC address to be written: %s\n", mac->str); parse_mac_string(); @@ -762,11 +837,14 @@ cmd_helper_setmac(void) void parse_mac_string(void) { + struct xstate *x = xstatus(); + struct macaddr *mac; + unsigned long mac_byte; - struct macaddr *mac = &nv->mac; + mac = &x->mac; - if (xstrxlen(nv->mac.str, 18) != 17) + if (xstrxlen(x->mac.str, 18) != 17) err(EINVAL, "MAC address is the wrong length"); memset(mac->mac_buf, 0, sizeof(mac->mac_buf)); @@ -811,11 +889,17 @@ xstrxlen(const char *scmp, unsigned long maxlen) void set_mac_byte(unsigned long mac_byte_pos) { - unsigned long mac_str_pos = mac_byte_pos * 3; - unsigned long mac_nib_pos; + struct xstate *x = xstatus(); + struct macaddr *mac; + char separator; - struct macaddr *mac = &nv->mac; + unsigned long mac_str_pos; + unsigned long mac_nib_pos; + + mac = &x->mac; + + mac_str_pos = mac_byte_pos * 3; if (mac_str_pos < 15) { if ((separator = mac->str[mac_str_pos + 2]) != ':') @@ -831,10 +915,13 @@ void set_mac_nib(unsigned long mac_str_pos, unsigned long mac_byte_pos, unsigned long mac_nib_pos) { + struct xstate *x = xstatus(); + struct macaddr *mac; + char mac_ch; unsigned short hex_num; - struct macaddr *mac = &nv->mac; + mac = &x->mac; mac_ch = mac->str[mac_str_pos + mac_nib_pos]; @@ -862,7 +949,9 @@ set_mac_nib(unsigned long mac_str_pos, unsigned short hextonum(char ch_s) { - unsigned char ch = (unsigned char)ch_s; + unsigned char ch; + + ch = (unsigned char)ch_s; if ((unsigned int)(ch - '0') <= 9) return ch - '0'; @@ -881,125 +970,59 @@ hextonum(char ch_s) unsigned long rlong(void) { - struct x_st_timeval tv; - static unsigned long mix = 0; - static unsigned long counter = 0; - - static int fd = -1; - unsigned long rval = 0; - long nr = -1; +#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__APPLE__) - x_i_gettimeofday(&tv, NULL); + unsigned long rval; + arc4random_buf(&rval, sizeof(unsigned long)); - mix ^= (unsigned long)tv.tv_sec - ^ (unsigned long)tv.tv_usec - ^ (unsigned long)getpid() - ^ (unsigned long)&mix - ^ counter++ - ^ entropy_jitter(); + return rval; +#else + int fd; - /* - * Stack addresses can vary between - * calls, thus increasing entropy. - */ - mix ^= (unsigned long)&mix; - mix ^= (unsigned long)&tv; - mix ^= (unsigned long)&counter; + long nr; - /* - * Now, we won't use this mix - * immediately. We'll try to - * read urandom first, which is - * likely safer, and pass that, - * falling back to the mixture - * if urandom fails. - * - * Since urandom is likely - * reliable, the number of - * times it will fail is - * likely extremely random, - * thus, building more than - * sufficient entropy by the - * time we do eventually use - * the fallback code - */ + unsigned long rval; - if (fd < 0) - fd = open("/dev/urandom", O_RDONLY | O_BINARY | O_NONBLOCK); + fd = open("/dev/urandom", O_RDONLY | O_BINARY); -#if !(defined(__OpenBSD__) && defined(OpenBSD)) || \ - (defined(__OpenBSD__) && defined(OpenBSD) && \ - OpenBSD < 604) +#ifdef __OpenBSD__ if (fd < 0) /* old openbsd */ - fd = open("/dev/arandom", O_RDONLY | O_BINARY | O_NONBLOCK); + fd = open("/dev/arandom", O_RDONLY | O_BINARY); #endif if (fd < 0) - fd = open("/dev/random", O_RDONLY | O_BINARY | O_NONBLOCK); + fd = open("/dev/random", O_RDONLY | O_BINARY); + + if (fd < 0) + err(errno, "can't open random device"); nr = rw_file_exact(fd, (unsigned char *)&rval, sizeof(unsigned long), 0, IO_READ, LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); - if (nr == sizeof(unsigned long)) - return rval; + if (x_i_close(fd) < 0) + err(errno, "Can't close randomness fd"); - return mix; -} - -unsigned long -entropy_jitter(void) -{ - struct x_st_timeval a, b; - unsigned long mix = 0; - long mix_diff; - int i; - - x_i_gettimeofday(&a, NULL); - - for (i = 0; i < 32; i++) { - getpid(); - x_i_gettimeofday(&b, NULL); - - /* - * prevent negative numbers to prevent overflow, - * which would bias rand to large numbers - */ - mix_diff = (long)(b.tv_usec - a.tv_usec); - if (mix_diff < 0) - mix_diff = -mix_diff; - - mix ^= (unsigned long)(mix_diff); - mix ^= (unsigned long)&mix; - } - - return mix; -} + if (nr != sizeof(unsigned long)) + err(errno, "Incomplete read from random device"); - - -int -x_i_gettimeofday(struct x_st_timeval *tv, void *tz) -{ - time_t t; - - (void)tz; - - t = time(NULL); - - tv->tv_sec = t; - tv->tv_usec = (long)((unsigned long)clock() % 1000000UL); - - return 0; + return rval; +#endif } void write_mac_part(unsigned long partnum) { + struct xstate *x = xstatus(); + struct xfile *f; + struct macaddr *mac; + unsigned long w; - struct xfile *f = &nv->f; - struct macaddr *mac = &nv->mac; + f = &x->f; + mac = &x->mac; check_bin(partnum, "part number"); if (!f->part_valid[partnum]) @@ -1016,25 +1039,35 @@ write_mac_part(unsigned long partnum) void cmd_helper_dump(void) { - unsigned long partnum; + struct xstate *x = xstatus(); + struct xfile *f; + + unsigned long p; + + f = &x->f; - struct xfile *f = &nv->f; + check_cmd(cmd_helper_dump, "dump"); f->part_valid[0] = good_checksum(0); f->part_valid[1] = good_checksum(1); - for (partnum = 0; partnum < 2; partnum++) { - if (!f->part_valid[partnum]) + for (p = 0; p < 2; p++) { + + if (!f->part_valid[p]) { + fprintf(stderr, "BAD checksum %04x in part %lu (expected %04x)\n", - nvm_word(NVM_CHECKSUM_WORD, partnum), - (unsigned long)partnum, - calculated_checksum(partnum)); + nvm_word(NVM_CHECKSUM_WORD, p), + (unsigned long)p, + calculated_checksum(p)); + } printf("MAC (part %lu): ", - (unsigned long)partnum); - print_mac_from_nvm(partnum); - hexdump(partnum); + (unsigned long)p); + + print_mac_from_nvm(p); + + hexdump(p); } } @@ -1064,15 +1097,23 @@ hexdump(unsigned long partnum) unsigned short val16; for (row = 0; row < 8; row++) { - printf("%08lx ", (unsigned long)((unsigned long)row << 4)); + + printf("%08lx ", + (unsigned long)((unsigned long)row << 4)); + for (c = 0; c < 8; c++) { + val16 = nvm_word((row << 3) + c, partnum); + if (c == 4) printf(" "); + printf(" %02x %02x", (unsigned int)(val16 & 0xff), (unsigned int)(val16 >> 8)); + } + printf("\n"); } } @@ -1080,7 +1121,12 @@ hexdump(unsigned long partnum) void cmd_helper_swap(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + check_cmd(cmd_helper_swap, "swap"); + + f = &x->f; x_v_memcpy( f->buf + (unsigned long)GBE_WORK_SIZE, @@ -1104,7 +1150,12 @@ cmd_helper_swap(void) void cmd_helper_copy(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + check_cmd(cmd_helper_copy, "copy"); + + f = &x->f; x_v_memcpy( f->buf + (unsigned long)((f->part ^ 1) * GBE_PART_SIZE), @@ -1117,44 +1168,105 @@ cmd_helper_copy(void) void cmd_helper_cat(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + check_cmd(cmd_helper_cat, "cat"); - unsigned long p = 0; - unsigned long ff = 0; - unsigned long nff = 0; + x->cat = 0; + cat(0); +} - unsigned long cmd_num = nv->i; +void +cmd_helper_cat16(void) +{ + struct xstate *x = xstatus(); + check_cmd(cmd_helper_cat16, "cat16"); - fflush(NULL); + x->cat = 1; + cat(1); +} - memset(f->pad, 0xff, GBE_PART_SIZE); +void +cmd_helper_cat128(void) +{ + struct xstate *x = xstatus(); + check_cmd(cmd_helper_cat128, "cat128"); - switch (cmd_num) { - case CMD_CAT: - nff = 0; - break; - case CMD_CAT16: - nff = 1; - break; - case CMD_CAT128: - nff = 15; - break; - default: - err(EINVAL, "erroneous call to cat"); + x->cat = 15; + cat(15); +} + +void +check_cmd(void (*fn)(void), + const char *name) +{ + struct xstate *x = xstatus(); + unsigned long i; + + if (x->cmd[x->i].run != fn) + err(ECANCELED, "Running %s, but cmd %s is set", + name, x->cmd[x->i].str); + + /* + * In addition to making sure we ran + * the right command, we now disable + * all commands from running again + * + * the _nop function will just call + * err() immediately + */ + + for (i = 0; i < items(x->cmd); i++) + x->cmd[i].run = cmd_helper_err; +} + +void +cmd_helper_err(void) +{ + err(ECANCELED, + "Erroneously running command twice"); +} + +void +cat(unsigned long nff) +{ + struct xstate *x = xstatus(); + struct xfile *f; + + unsigned long p; + unsigned long ff; + + f = &x->f; + + p = 0; + ff = 0; + + if ((unsigned long)x->cat != nff) { + + err(ECANCELED, "erroneous call to cat"); } + fflush(NULL); + + memset(f->pad, 0xff, GBE_PART_SIZE); + for (p = 0; p < 2; p++) { + cat_buf(f->bufcmp + (unsigned long)(p * (f->gbe_file_size >> 1))); - for (ff = 0; ff < nff; ff++) + for (ff = 0; ff < nff; ff++) { + cat_buf(f->pad); + } } } void cat_buf(unsigned char *b) { + if (b == NULL) + err(errno, "null pointer in cat command"); + if (rw_file_exact(STDOUT_FILENO, b, GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR) < 0) @@ -1164,16 +1276,20 @@ cat_buf(unsigned char *b) void write_gbe_file(void) { + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; + struct stat _gbe_st; struct stat _tmp_st; unsigned long p; unsigned char update_checksum; - struct commands *cmd = &nv->cmd[nv->i]; - struct xfile *f = &nv->f; + cmd = &x->cmd[x->i]; + f = &x->f; - if (cmd->flags == O_RDONLY) + if ((cmd->flags & O_ACCMODE) == O_RDONLY) return; if (fstat(f->gbe_fd, &_gbe_st) == -1) @@ -1218,7 +1334,9 @@ unsigned short calculated_checksum(unsigned long p) { unsigned long c; - unsigned int val16 = 0; + unsigned int val16; + + val16 = 0; for (c = 0; c < NVM_CHECKSUM_WORD; c++) val16 += (unsigned int)nvm_word(c, p); @@ -1237,10 +1355,13 @@ calculated_checksum(unsigned long p) unsigned short nvm_word(unsigned long pos16, unsigned long p) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; unsigned long pos; + f = &x->f; + check_nvm_bound(pos16, p); pos = (pos16 << 1) + (p * GBE_PART_SIZE); @@ -1251,9 +1372,13 @@ nvm_word(unsigned long pos16, unsigned long p) void set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + unsigned long pos; + f = &x->f; + check_nvm_bound(pos16, p); pos = (pos16 << 1) + (p * GBE_PART_SIZE); @@ -1266,7 +1391,10 @@ set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16) void set_part_modified(unsigned long p) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + f = &x->f; check_bin(p, "part number"); f->part_modified[p] = 1; @@ -1300,15 +1428,19 @@ void rw_gbe_file_part(unsigned long p, int rw_type, const char *rw_type_str) { - struct xfile *f = &nv->f; - struct commands *cmd = &nv->cmd[nv->i]; + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; - long r; - unsigned long gbe_rw_size; + long rval; + off_t file_offset; + + unsigned long gbe_rw_size; unsigned char *mem_offset; - off_t file_offset; + cmd = &x->cmd[x->i]; + f = &x->f; gbe_rw_size = cmd->rw_size; @@ -1319,14 +1451,14 @@ rw_gbe_file_part(unsigned long p, int rw_type, mem_offset = gbe_mem_offset(p, rw_type_str); file_offset = (off_t)gbe_file_offset(p, rw_type_str); - r = rw_gbe_file_exact(f->tmp_fd, mem_offset, + rval = rw_gbe_file_exact(f->tmp_fd, mem_offset, gbe_rw_size, file_offset, rw_type); - if (r == -1) + if (rval == -1) err(errno, "%s: %s: part %lu", f->fname, rw_type_str, (unsigned long)p); - if ((unsigned long)r != gbe_rw_size) + if ((unsigned long)rval != gbe_rw_size) err(EIO, "%s: partial %s: part %lu", f->fname, rw_type_str, (unsigned long)p); } @@ -1334,14 +1466,17 @@ rw_gbe_file_part(unsigned long p, int rw_type, void write_to_gbe_bin(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; int saved_errno; int mv; - struct commands *cmd = &nv->cmd[nv->i]; + cmd = &x->cmd[x->i]; + f = &x->f; - if (cmd->flags != O_RDWR) + if ((cmd->flags & O_ACCMODE) != O_RDWR) return; write_gbe_file(); @@ -1426,23 +1561,28 @@ write_to_gbe_bin(void) void check_written_part(unsigned long p) { - struct commands *cmd = &nv->cmd[nv->i]; - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct commands *cmd; + struct xfile *f; + + long rval; unsigned long gbe_rw_size; - unsigned char *mem_offset; + off_t file_offset; - unsigned char *buf_restore; + unsigned char *mem_offset; + struct stat st; + unsigned char *buf_restore; - long r; + cmd = &x->cmd[x->i]; + f = &x->f; if (!f->part_modified[p]) return; gbe_rw_size = cmd->rw_size; - /* invert not needed for pwrite */ mem_offset = gbe_mem_offset(p, "pwrite"); file_offset = (off_t)gbe_file_offset(p, "pwrite"); @@ -1458,12 +1598,12 @@ check_written_part(unsigned long p) if (st.st_dev != f->tmp_dev || st.st_ino != f->tmp_ino) err(EIO, "%s: file changed during write", f->tname); - r = rw_gbe_file_exact(f->tmp_fd, f->pad, + rval = rw_gbe_file_exact(f->tmp_fd, f->pad, gbe_rw_size, file_offset, IO_PREAD); - if (r == -1) + if (rval == -1) f->rw_check_err_read[p] = f->io_err_gbe = 1; - else if ((unsigned long)r != gbe_rw_size) + else if ((unsigned long)rval != gbe_rw_size) f->rw_check_partial_read[p] = f->io_err_gbe = 1; else if (x_i_memcmp(mem_offset, f->pad, gbe_rw_size) != 0) f->rw_check_bad_part[p] = f->io_err_gbe = 1; @@ -1478,18 +1618,29 @@ check_written_part(unsigned long p) * That's why we hardcode good_checksum(0). */ buf_restore = f->buf; + + /* + * good_checksum works on f->buf + * so let's change f->buf for now + */ f->buf = f->pad; - f->post_rw_checksum[p] = good_checksum(0); + + if (good_checksum(0)) + f->post_rw_checksum[p] = 1; + f->buf = buf_restore; } void report_io_err_rw(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; unsigned long p; + f = &x->f; + if (!f->io_err_gbe) return; @@ -1541,20 +1692,30 @@ report_io_err_rw(void) int gbe_mv(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + int rval; - int r; int saved_errno; - int tmp_gbe_bin_exists = 1; + int tmp_gbe_bin_exists; + + char *dest_tmp; + int dest_fd; - char *dest_tmp = NULL; - int dest_fd = -1; + f = &x->f; + + /* will be set 0 if it doesn't */ + tmp_gbe_bin_exists = 1; + + dest_tmp = NULL; + dest_fd = -1; saved_errno = errno; - r = rename(f->tname, f->fname); + rval = rename(f->tname, f->fname); - if (r > -1) { + if (rval > -1) { /* * same filesystem */ @@ -1563,7 +1724,7 @@ gbe_mv(void) if (fsync_dir(f->fname) < 0) { f->io_err_gbe_bin = 1; - r = -1; + rval = -1; } goto ret_gbe_mv; @@ -1574,7 +1735,7 @@ gbe_mv(void) /* cross-filesystem rename */ - if ((r = f->tmp_fd = open(f->tname, + if ((rval = f->tmp_fd = open(f->tname, O_RDONLY | O_BINARY)) == -1) goto ret_gbe_mv; @@ -1585,20 +1746,20 @@ gbe_mv(void) /* copy data */ - r = rw_file_exact(f->tmp_fd, f->bufcmp, + rval = rw_file_exact(f->tmp_fd, f->bufcmp, f->gbe_file_size, 0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); - if (r < 0) + if (rval < 0) goto ret_gbe_mv; - r = rw_file_exact(dest_fd, f->bufcmp, + rval = rw_file_exact(dest_fd, f->bufcmp, f->gbe_file_size, 0, IO_PWRITE, NO_LOOP_EAGAIN, LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR); - if (r < 0) + if (rval < 0) goto ret_gbe_mv; if (x_i_fsync(dest_fd) == -1) @@ -1622,17 +1783,17 @@ ret_gbe_mv: if (f->gbe_fd > -1) { if (x_i_close(f->gbe_fd) < 0) - r = -1; + rval = -1; if (fsync_dir(f->fname) < 0) { f->io_err_gbe_bin = 1; - r = -1; + rval = -1; } f->gbe_fd = -1; } if (f->tmp_fd > -1) { if (x_i_close(f->tmp_fd) < 0) - r = -1; + rval = -1; f->tmp_fd = -1; } @@ -1643,12 +1804,12 @@ ret_gbe_mv: */ if (tmp_gbe_bin_exists) { if (unlink(f->tname) < 0) - r = -1; + rval = -1; else tmp_gbe_bin_exists = 0; } - if (r < 0) { + if (rval < 0) { /* * if nothing set errno, * we assume EIO, or we @@ -1660,7 +1821,7 @@ ret_gbe_mv: errno = saved_errno; } - return r; + return rval; } /* @@ -1670,21 +1831,27 @@ ret_gbe_mv: int fsync_dir(const char *path) { -#if defined(PATH_LEN) && \ - (PATH_LEN) >= 256 - unsigned long maxlen = PATH_LEN; -#else - unsigned long maxlen = 1024; -#endif + int saved_errno = errno; + unsigned long pathlen; -/* char dirbuf[maxlen]; */ - char *dirbuf = NULL; + unsigned long maxlen; + + char *dirbuf; + int dirfd; + char *slash; - int dfd = -1; struct stat st; - int saved_errno = errno; +#if defined(PATH_LEN) && \ + (PATH_LEN) >= 256 + maxlen = PATH_LEN; +#else + maxlen = 1024; +#endif + + dirbuf = NULL; + dirfd = -1; pathlen = xstrxlen(path, maxlen); @@ -1717,7 +1884,7 @@ fsync_dir(const char *path) dirbuf[1] = '\0'; } - dfd = open(dirbuf, O_RDONLY + dirfd = open(dirbuf, O_RDONLY #ifdef O_DIRECTORY | O_DIRECTORY #endif @@ -1725,10 +1892,10 @@ fsync_dir(const char *path) | O_NOFOLLOW #endif ); - if (dfd == -1) + if (dirfd == -1) goto err_fsync_dir; - if (fstat(dfd, &st) < 0) + if (fstat(dirfd, &st) < 0) goto err_fsync_dir; if (!S_ISDIR(st.st_mode)) { @@ -1737,10 +1904,10 @@ fsync_dir(const char *path) } /* sync file on disk */ - if (x_i_fsync(dfd) == -1) + if (x_i_fsync(dirfd) == -1) goto err_fsync_dir; - if (x_i_close(dfd) == -1) + if (x_i_close(dirfd) == -1) goto err_fsync_dir; if (dirbuf != NULL) @@ -1759,8 +1926,8 @@ err_fsync_dir: if (dirbuf != NULL) free(dirbuf); - if (dfd > -1) - x_i_close(dfd); + if (dirfd > -1) + x_i_close(dirfd); errno = saved_errno; @@ -1775,12 +1942,18 @@ err_fsync_dir: unsigned char * gbe_mem_offset(unsigned long p, const char *f_op) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + off_t gbe_off; + + f = &x->f; - off_t gbe_off = gbe_x_offset(p, f_op, "mem", + gbe_off = gbe_x_offset(p, f_op, "mem", GBE_PART_SIZE, GBE_WORK_SIZE); - return (unsigned char *)(f->buf + (unsigned long)gbe_off); + return (unsigned char *) + (f->buf + (unsigned long)gbe_off); } /* @@ -1793,9 +1966,14 @@ gbe_mem_offset(unsigned long p, const char *f_op) off_t gbe_file_offset(unsigned long p, const char *f_op) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; + + off_t gbe_file_half_size; + + f = &x->f; - off_t gbe_file_half_size = f->gbe_file_size >> 1; + gbe_file_half_size = f->gbe_file_size >> 1; return gbe_x_offset(p, f_op, "file", gbe_file_half_size, f->gbe_file_size); @@ -1805,12 +1983,15 @@ off_t gbe_x_offset(unsigned long p, const char *f_op, const char *d_type, off_t nsize, off_t ncmp) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; off_t off; check_bin(p, "part number"); + f = &x->f; + off = ((off_t)p) * (off_t)nsize; if (off > ncmp - GBE_PART_SIZE) @@ -1828,10 +2009,13 @@ long rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw, off_t off, int rw_type) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; long r; + f = &x->f; + if (io_args(fd, mem, nrw, off, rw_type) == -1) return -1; @@ -1899,23 +2083,31 @@ rw_file_exact(int fd, unsigned char *mem, unsigned long nrw, int loop_eintr, unsigned long max_retries, int off_reset) { - long rv = 0; - long rc = 0; - unsigned long retries_on_zero = 0; - off_t off_cur; + long rval; + long rc; + unsigned long nrw_cur; + + off_t off_cur; void *mem_cur; + unsigned long retries_on_zero; + + rval = 0; + + rc = 0; + retries_on_zero = 0; + if (io_args(fd, mem, nrw, off, rw_type) == -1) return -1; while (1) { /* Prevent theoretical overflow */ - if (rv >= 0 && (unsigned long)rv > (nrw - rc)) + if (rval >= 0 && (unsigned long)rval > (nrw - rc)) goto err_rw_file_exact; - rc += rv; + rc += rval; if ((unsigned long)rc >= nrw) break; @@ -1925,14 +2117,14 @@ rw_file_exact(int fd, unsigned char *mem, unsigned long nrw, goto err_rw_file_exact; off_cur = off + (off_t)rc; - rv = prw(fd, mem_cur, nrw_cur, off_cur, + rval = prw(fd, mem_cur, nrw_cur, off_cur, rw_type, loop_eagain, loop_eintr, off_reset); - if (rv < 0) + if (rval < 0) return -1; - if (rv == 0) { + if (rval == 0) { if (retries_on_zero++ < max_retries) continue; goto err_rw_file_exact; @@ -1999,6 +2191,12 @@ prw(int fd, void *mem, unsigned long nrw, int loop_eagain, int loop_eintr, int off_reset) { +#ifndef MAX_EAGAIN_RETRIES + unsigned long retries = 100000; +#else + unsigned long retries = MAX_EAGAIN_RETRIES; +#endif + long r; int positional_rw; struct stat st; @@ -2010,8 +2208,10 @@ prw(int fd, void *mem, unsigned long nrw, off_t off_last; #endif - if (io_args(fd, mem, nrw, off, rw_type) == -1) + if (io_args(fd, mem, nrw, off, rw_type) + == -1) { return -1; + } r = -1; @@ -2124,8 +2324,9 @@ real_pread_pwrite: } } while (r == -1 && - (errno == try_err(loop_eintr, EINTR) - || errno == try_err(loop_eagain, EAGAIN))); + (errno == try_err(loop_eintr, EINTR) || + errno == try_err(loop_eagain, EAGAIN)) && + retries++ < MAX_EAGAIN_RETRIES); } saved_errno = errno; @@ -2222,7 +2423,9 @@ rw_over_nrw(long r, unsigned long nrw) if (r == -1) return r; - if ((unsigned long)r > X_LONG_MAX) { + if ((unsigned long) + r > X_LONG_MAX) { + /* * Theoretical buggy libc * check. Extremely academic. @@ -2265,7 +2468,9 @@ off_t lseek_loop(int fd, off_t off, int whence, int loop_eagain, int loop_eintr) { - off_t old = -1; + off_t old; + + old = -1; do { old = lseek(fd, off, whence); @@ -2298,7 +2503,9 @@ try_err(int loop_err, int errval) void usage(void) { - const char *util = getnvmprogname(); + const char *util; + + util = getnvmprogname(); fprintf(stderr, "Modify Intel GbE NVM images e.g. set MAC\n" @@ -2319,7 +2526,7 @@ usage(void) void err(int nvm_errval, const char *msg, ...) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); va_list args; @@ -2330,18 +2537,14 @@ err(int nvm_errval, const char *msg, ...) (void)exit_cleanup(); - fprintf(stderr, "%s: ", getnvmprogname()); + if (x != NULL) + fprintf(stderr, "%s: ", getnvmprogname()); va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); - fprintf(stderr, ": %s", strerror(errno)); - - fprintf(stderr, "\n"); - - if (f->tname != NULL) - free(f->tname); + fprintf(stderr, ": %s\n", strerror(errno)); exit(EXIT_FAILURE); } @@ -2349,28 +2552,36 @@ err(int nvm_errval, const char *msg, ...) int exit_cleanup(void) { - struct xfile *f = &nv->f; + struct xstate *x = xstatus(); + struct xfile *f; - int close_err = 0; - int saved_errno = errno; + int close_err; + int saved_errno; - if (f->gbe_fd > -1) { - if (x_i_close(f->gbe_fd) == -1) - close_err = 1; - f->gbe_fd = -1; - } + close_err = 0; + saved_errno = errno; - if (f->tmp_fd > -1) { - if (x_i_close(f->tmp_fd) == -1) - close_err = 1; - } + if (x != NULL) { + f = &x->f; - if (f->tname != NULL) { - if (unlink(f->tname) == -1) - close_err = 1; - } + if (f->gbe_fd > -1) { + if (x_i_close(f->gbe_fd) == -1) + close_err = 1; + f->gbe_fd = -1; + } - f->tmp_fd = -1; + if (f->tmp_fd > -1) { + if (x_i_close(f->tmp_fd) == -1) + close_err = 1; + } + + if (f->tname != NULL) { + if (unlink(f->tname) == -1) + close_err = 1; + } + + f->tmp_fd = -1; + } if (saved_errno) errno = saved_errno; @@ -2384,17 +2595,25 @@ exit_cleanup(void) const char * getnvmprogname(void) { + struct xstate *x = xstatus(); const char *p; + static char fallback[] = "nvmutil"; - if (nv->argv0 == NULL || *nv->argv0 == '\0') - return ""; + char *rval = fallback; - p = x_c_strrchr(nv->argv0, '/'); + if (x != NULL) { + if (x->argv0 == NULL || *x->argv0 == '\0') + return ""; + + rval = x->argv0; + } + + p = x_c_strrchr(rval, '/'); if (p) return p + 1; else - return nv->argv0; + return rval; } /* @@ -2424,8 +2643,6 @@ getnvmprogname(void) char * new_tmpfile(int *fd, int local, const char *path) { - struct xfile *f = &nv->f; - unsigned long maxlen; struct stat st; @@ -2487,7 +2704,7 @@ new_tmpfile(int *fd, int local, const char *path) /* * appended to filename for tmp: */ - tmpdir_len = sizeof(default_tmpname); + tmpdir_len = xstrxlen(default_tmpname, maxlen); } else { base = x_c_tmpdir(); @@ -2539,10 +2756,23 @@ new_tmpfile(int *fd, int local, const char *path) if (fd_tmp == -1) goto err_new_tmpfile; - if (x_i_fchmod(fd_tmp, 0600) == -1) + if (fchmod(fd_tmp, 0600) == -1) + goto err_new_tmpfile; + + flags = fcntl(fd_tmp, F_GETFL); + + if (flags == -1) goto err_new_tmpfile; - if (lock_file(fd_tmp) == -1) + /* + * O_APPEND would permit offsets + * to be ignored, which breaks + * positional read/write + */ + if (flags & O_APPEND) + goto err_new_tmpfile; + + if (lock_file(fd_tmp, flags) == -1) goto err_new_tmpfile; if (fstat(fd_tmp, &st) == -1) @@ -2561,10 +2791,6 @@ new_tmpfile(int *fd, int local, const char *path) if (lseek(fd_tmp, 0, SEEK_CUR) == (off_t)-1) goto err_new_tmpfile; - /* inode will be checked later on write */ - f->tmp_dev = st.st_dev; - f->tmp_ino = st.st_ino; - /* tmpfile has >1 hardlinks */ if (st.st_nlink > 1) goto err_new_tmpfile; @@ -2573,19 +2799,6 @@ new_tmpfile(int *fd, int local, const char *path) if (st.st_nlink == 0) goto err_new_tmpfile; - flags = fcntl(fd_tmp, F_GETFL); - - if (flags == -1) - goto err_new_tmpfile; - - /* - * O_APPEND would permit offsets - * to be ignored, which breaks - * positional read/write - */ - if (flags & O_APPEND) - goto err_new_tmpfile; - *fd = fd_tmp; return dest; @@ -2612,8 +2825,10 @@ x_i_mkstemp(char *template) unsigned long len; char *p; - char ch[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - unsigned long r = rlong(); + char ch[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + unsigned long r; len = xstrxlen(template, PATH_LEN); @@ -2625,8 +2840,10 @@ x_i_mkstemp(char *template) for (i = 0; i < 100; i++) { - for (j = 0; j < 6; j++) - p[j] = ch[r % (sizeof(ch) - 1)]; + for (j = 0; j < 6; j++) { + r = rlong(); + p[j] = ch[(unsigned long)(r >> 1) % (sizeof(ch) - 1)]; + } fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); @@ -2659,10 +2876,17 @@ x_c_strrchr(const char *s, int c) } /* + * non-atomic rename + * + * commented because i can't sacrifice + * exactly this property. nvmutil tries + * to protect files against e.g. power loss + */ +/* int x_i_rename(const char *src, const char *dst) { - int sfd, dfd; + int sfd, dirfd; ssize_t r; char buf[8192]; @@ -2670,31 +2894,31 @@ x_i_rename(const char *src, const char *dst) if (sfd < 0) return -1; - dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (dfd < 0) { + dirfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (dirfd < 0) { x_i_close(sfd); return -1; } while ((r = read(sfd, buf, sizeof(buf))) > 0) { - ssize_t w = write(dfd, buf, r); + ssize_t w = write(dirfd, buf, r); if (w != r) { x_i_close(sfd); - x_i_close(dfd); + x_i_close(dirfd); return -1; } } if (r < 0) { x_i_close(sfd); - x_i_close(dfd); + x_i_close(dirfd); return -1; } - x_i_fsync(dfd); + x_i_fsync(dirfd); x_i_close(sfd); - x_i_close(dfd); + x_i_close(dirfd); if (unlink(src) < 0) return -1; @@ -2710,9 +2934,13 @@ x_c_tmpdir(void) struct stat st; t = getenv("TMPDIR"); + t = getenv("TMPDIR"); if (t && *t) { - if (stat(t, &st) == 0 && S_ISDIR(st.st_mode)) + if (stat(t, &st) == 0 && S_ISDIR(st.st_mode)) { + if ((st.st_mode & S_IWOTH) && !(st.st_mode & S_ISVTX)) + return NULL; return t; + } } if (stat("/tmp", &st) == 0 && S_ISDIR(st.st_mode)) @@ -2728,11 +2956,15 @@ int x_i_close(int fd) { int r; + int saved_errno = errno; do { r = close(fd); } while (r == -1 && errno == EINTR); + if (r > -1) + errno = saved_errno; + return r; } @@ -2754,90 +2986,13 @@ x_i_memcmp(const void *a, const void *b, unsigned long n) const unsigned char *pa = (const unsigned char *)a; const unsigned char *pb = (const unsigned char *)b; - while (n--) { + for ( ; n--; ++pa, ++pb) if (*pa != *pb) return *pa - *pb; - pa++; - pb++; - } return 0; } -/* - * emulate fchmod() using file descriptor - * paths, for old unix portability. should - * work on e.g. BSD/MacOS (/dev/fd/N), - * Linux (/proc/self/fd/N) and others - */ -int -x_i_fchmod(int fd, mode_t mode) -{ - if (x_try_fdpath("/dev/fd/", fd, mode) == 0) - return 0; - - if (x_try_fdpath("/proc/self/fd/", fd, mode) == 0) - return 0; - - errno = ENOSYS; - return -1; -} - -int -x_try_fdpath(const char *prefix, int fd, mode_t mode) -{ - char path[PATH_LEN]; - - unsigned long i = 0; - unsigned long j; - - struct stat st; - - while (prefix[i]) { - if (i >= PATH_LEN - 1) - return -1; - path[i] = prefix[i]; - i++; - } - - j = x_conv_fd(path + i, (unsigned long)fd); - - if (i + j >= PATH_LEN) - return -1; - - i += j; - path[i] = '\0'; - - if (stat(path, &st) < 0) - return -1; - - return chmod(path, mode); -} - -unsigned long -x_conv_fd(char *buf, unsigned long n) -{ - char tmp[256]; - - unsigned long i = 0; - unsigned long j = 0; - - if (n == 0) { - buf[0] = '0'; - return 1; - } - - while (n > 0) { - tmp[i++] = (char)('0' + (n % 10)); - n /= 10; - } - - while (i > 0) - buf[j++] = tmp[--i]; - - return j; -} - int x_i_fsync(int fd) { @@ -2849,63 +3004,3 @@ x_i_fsync(int fd) return r; } - -/* type asserts */ -typedef char static_assert_char_is_8_bits[(CHAR_BIT == 8) ? 1 : -1]; -typedef char static_assert_char_is_1[(sizeof(char) == 1) ? 1 : -1]; -typedef char static_assert_unsigned_char_is_1[ - (sizeof(unsigned char) == 1) ? 1 : -1]; -typedef char static_assert_unsigned_short_is_2[ - (sizeof(unsigned short) >= 2) ? 1 : -1]; -typedef char static_assert_short_is_2[(sizeof(short) >= 2) ? 1 : -1]; -typedef char static_assert_unsigned_int_is_4[ - (sizeof(unsigned int) >= 4) ? 1 : -1]; -typedef char static_assert_unsigned_long_is_4[ - (sizeof(unsigned long) >= 4) ? 1 : -1]; -typedef char static_assert_int_ge_32[(sizeof(int) >= 4) ? 1 : -1]; -typedef char static_assert_twos_complement[ - ((-1 & 3) == 3) ? 1 : -1 -]; -typedef char assert_unsigned_long_ptr[ - (sizeof(unsigned long) >= sizeof(void *)) ? 1 : -1 -]; - -/* - * We set _FILE_OFFSET_BITS 64, but we only handle - * but we only need smaller files, so require 4-bytes. - * Some operating systems ignore the define, hence assert: - */ -typedef char static_assert_off_t_is_32[(sizeof(off_t) >= 4) ? 1 : -1]; - -/* - * asserts (variables/defines sanity check) - */ -typedef char assert_argc3[(ARGC_3==3)?1:-1]; -typedef char assert_argc4[(ARGC_4==4)?1:-1]; -typedef char assert_read[(IO_READ==0)?1:-1]; -typedef char assert_write[(IO_WRITE==1)?1:-1]; -typedef char assert_pread[(IO_PREAD==2)?1:-1]; -typedef char assert_pwrite[(IO_PWRITE==3)?1:-1]; -typedef char assert_pathlen[(PATH_LEN>=256)?1:-1]; -/* commands */ -typedef char assert_cmd_dump[(CMD_DUMP==0)?1:-1]; -typedef char assert_cmd_setmac[(CMD_SETMAC==1)?1:-1]; -typedef char assert_cmd_swap[(CMD_SWAP==2)?1:-1]; -typedef char assert_cmd_copy[(CMD_COPY==3)?1:-1]; -typedef char assert_cmd_cat[(CMD_CAT==4)?1:-1]; -typedef char assert_cmd_cat16[(CMD_CAT16==5)?1:-1]; -typedef char assert_cmd_cat128[(CMD_CAT128==6)?1:-1]; -/* bool */ -typedef char bool_arg_nopart[(ARG_NOPART==0)?1:-1]; -typedef char bool_arg_part[(ARG_PART==1)?1:-1]; -typedef char bool_skip_checksum_read[(SKIP_CHECKSUM_READ==0)?1:-1]; -typedef char bool_checksum_read[(CHECKSUM_READ==1)?1:-1]; -typedef char bool_skip_checksum_write[(SKIP_CHECKSUM_WRITE==0)?1:-1]; -typedef char bool_checksum_write[(CHECKSUM_WRITE==1)?1:-1]; -typedef char bool_loop_eintr[(LOOP_EINTR==1||LOOP_EINTR==0)?1:-1]; -typedef char bool_loop_eagain[(LOOP_EAGAIN==1||LOOP_EAGAIN==0)?1:-1]; -typedef char bool_no_loop_eintr[(NO_LOOP_EINTR==0)?1:-1]; -typedef char bool_no_loop_eagain[(NO_LOOP_EAGAIN==0)?1:-1]; -typedef char bool_off_err[(OFF_ERR==0)?1:-1]; -typedef char bool_off_reset[(OFF_RESET==0||OFF_RESET==1)?1:-1]; - |
