diff options
| -rw-r--r-- | util/nvmutil/Makefile | 37 | ||||
| -rw-r--r-- | util/nvmutil/nvmutil.c | 239 | ||||
| -rw-r--r-- | util/nvmutil/nvmutil.h | 71 |
3 files changed, 202 insertions, 145 deletions
diff --git a/util/nvmutil/Makefile b/util/nvmutil/Makefile index 025e87e1..6488ca43 100644 --- a/util/nvmutil/Makefile +++ b/util/nvmutil/Makefile @@ -3,25 +3,42 @@ # Copyright (c) 2023 Riku Viitanen <riku.viitanen@protonmail.com> CC?=cc -CSTD?=-std=c90 -WERROR?=-Werror -CWARN?=-Wall -Wextra -pedantic -COPT?=-Os -CFLAGS?=$(CWARN) $(CSTD) +HELLCC?=clang + +CFLAGS?= LDFLAGS?= DESTDIR?= PREFIX?=/usr/local INSTALL?=install -LDIR?=-I. -OPTS=$(LDIR) $(COPT) $(WERROR) $(CFLAGS) $(LDFLAGS) +.SUFFIXES: + +# maybe add -I. here when running make +# e.g. make LDIR=-I. +LDIR?= + +PORTABLE?=$(LDIR) $(CFLAGS) +WARN?=$(PORTABLE) -Wall -Wextra +STRICT?=$(WARN) -std=c90 -pedantic -Werror +HELLFLAGS?=$(STRICT) -Weverything +# program name PROG=nvmutil all: $(PROG) -$(PROG): nvmutil.c - $(CC) $(OPTS) nvmutil.c -o $(PROG) +$(PROG): $(PROG).c + $(CC) $(PORTABLE) $(PROG).c -o $(PROG) $(LDFLAGS) + +warn: $(PROG).c + $(CC) $(WARN) $(PROG).c -o $(PROG) $(LDFLAGS) + +strict: $(PROG).c + $(CC) $(STRICT) $(PROG).c -o $(PROG) $(LDFLAGS) + +# clang-only extreme warnings (not portable) +hell: $(PROG).c + $(HELLCC) $(HELLFLAGS) $(PROG).c -o $(PROG) $(LDFLAGS) install: $(PROG) $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin @@ -36,4 +53,4 @@ clean: distclean: clean -.PHONY: all install uninstall clean distclean +.PHONY: all warn strict hell install uninstall clean distclean diff --git a/util/nvmutil/nvmutil.c b/util/nvmutil/nvmutil.c index f6374a7a..f938d2cb 100644 --- a/util/nvmutil/nvmutil.c +++ b/util/nvmutil/nvmutil.c @@ -94,7 +94,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) */ @@ -151,23 +151,22 @@ main(int argc, char *argv[]) { struct commands *cmd; struct xfile *f; + int fd; + struct stat st; - unsigned long *i; - - nv = new_xstate(); - if (nv == NULL) - err(errno, NULL); + char *tmp_path = NULL; - nv->argv0 = argv[0]; - if (argc < 3) - usage(); + unsigned long *i; +#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,12 +174,12 @@ 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 @@ -197,6 +196,26 @@ main(int argc, char *argv[]) #endif #endif + nv = new_xstate(); + + if (nv == NULL) + err(errno, NULL); + if (nv->f.buf == NULL) + err(EINVAL, "Work buffer not initialised"); + + nv->argv0 = argv[0]; + f = &nv->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); @@ -207,11 +226,11 @@ main(int argc, char *argv[]) #ifdef NVMUTIL_UNVEIL if (cmd->flags == O_RDONLY) { - if (unveil(fname, "r") == -1) - err(errno, "%s: unveil r", fname); + 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) @@ -506,19 +525,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 +549,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) { @@ -1442,7 +1475,6 @@ check_written_part(unsigned long p) 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"); @@ -2319,8 +2351,6 @@ usage(void) void err(int nvm_errval, const char *msg, ...) { - struct xfile *f = &nv->f; - va_list args; if (errno == 0) @@ -2336,12 +2366,7 @@ err(int nvm_errval, const char *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 +2374,32 @@ err(int nvm_errval, const char *msg, ...) int exit_cleanup(void) { - struct xfile *f = &nv->f; + struct xfile *f; int close_err = 0; int saved_errno = errno; - if (f->gbe_fd > -1) { - if (x_i_close(f->gbe_fd) == -1) - close_err = 1; - f->gbe_fd = -1; - } + if (nv != NULL) { + f = &nv->f; - if (f->tmp_fd > -1) { - if (x_i_close(f->tmp_fd) == -1) - close_err = 1; - } + if (f->gbe_fd > -1) { + if (x_i_close(f->gbe_fd) == -1) + close_err = 1; + f->gbe_fd = -1; + } - if (f->tname != NULL) { - if (unlink(f->tname) == -1) - close_err = 1; - } + if (f->tmp_fd > -1) { + if (x_i_close(f->tmp_fd) == -1) + close_err = 1; + } - f->tmp_fd = -1; + if (f->tname != NULL) { + if (unlink(f->tname) == -1) + close_err = 1; + } + + f->tmp_fd = -1; + } if (saved_errno) errno = saved_errno; @@ -2424,8 +2453,6 @@ getnvmprogname(void) char * new_tmpfile(int *fd, int local, const char *path) { - struct xfile *f = &nv->f; - unsigned long maxlen; struct stat st; @@ -2542,7 +2569,20 @@ new_tmpfile(int *fd, int local, const char *path) if (x_i_fchmod(fd_tmp, 0600) == -1) goto err_new_tmpfile; - if (lock_file(fd_tmp) == -1) + 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; + + if (lock_file(fd_tmp, flags) == -1) goto err_new_tmpfile; if (fstat(fd_tmp, &st) == -1) @@ -2561,10 +2601,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 +2609,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 +2635,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 +2650,10 @@ x_i_mkstemp(char *template) for (i = 0; i < 100; i++) { - for (j = 0; j < 6; j++) + for (j = 0; j < 6; j++) { + r = rlong(); p[j] = ch[r % (sizeof(ch) - 1)]; + } fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); @@ -2659,6 +2686,13 @@ 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) { @@ -2754,12 +2788,9 @@ 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; } @@ -2849,63 +2880,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]; - diff --git a/util/nvmutil/nvmutil.h b/util/nvmutil/nvmutil.h index 20e66dd9..c278481e 100644 --- a/util/nvmutil/nvmutil.h +++ b/util/nvmutil/nvmutil.h @@ -66,6 +66,10 @@ #define EXIT_SUCCESS 0 #endif +#ifndef O_ACCMODE +#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -311,7 +315,7 @@ int xstrxcmp(const char *a, const char *b, unsigned long maxlen); * Prep files for reading */ void open_gbe_file(void); -int lock_file(int fd); +int lock_file(int fd, int flags); void xopen(int *fd, const char *path, int flags, struct stat *st); /* @@ -462,4 +466,69 @@ unsigned long x_conv_fd(char *buf, unsigned long n); int x_i_fsync(int fd); + + + +/* asserts */ + +/* 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]; + + #endif |
