mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-03-25 21:39:03 +02:00
Compare commits
63 Commits
master
...
mkhtemp-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5a08963c3 | ||
|
|
e9cefe81b0 | ||
|
|
6db9514c95 | ||
|
|
49cc239884 | ||
|
|
7bff5712b4 | ||
|
|
a6da25ad0b | ||
|
|
f7f1856969 | ||
|
|
e1ff02f323 | ||
|
|
5f93b7faec | ||
|
|
7efdc40cab | ||
|
|
1228f7e0e5 | ||
|
|
f06db344ad | ||
|
|
3ddd7a0d36 | ||
|
|
2cee58188c | ||
|
|
1ed2ca6b69 | ||
|
|
4fc4946f3c | ||
|
|
f8d9c51a36 | ||
|
|
b8a045ef86 | ||
|
|
715723c7ce | ||
|
|
b16bb6c445 | ||
|
|
75f03ea696 | ||
|
|
88ff5f7380 | ||
|
|
bf5a3df796 | ||
|
|
3522a23587 | ||
|
|
217ad55bed | ||
|
|
c1befbcd3e | ||
|
|
6593e76c6a | ||
|
|
e9c5da1a25 | ||
|
|
56ab5a18fe | ||
|
|
9de01208b0 | ||
|
|
cce396a1ac | ||
|
|
e7ede0c755 | ||
|
|
616099edad | ||
|
|
61ee661b88 | ||
|
|
8b8a5635c3 | ||
|
|
2c21a04741 | ||
|
|
a5eed39a76 | ||
|
|
f2544d094b | ||
|
|
afcd535816 | ||
|
|
a9165d2503 | ||
|
|
ba80191b78 | ||
|
|
8261553818 | ||
|
|
7c414b2d84 | ||
|
|
024862a152 | ||
|
|
c1c1aee695 | ||
|
|
f18f995c42 | ||
|
|
186d612ac7 | ||
|
|
1f7d4c72f4 | ||
|
|
c87e425442 | ||
|
|
1684f475cd | ||
|
|
cf4be895f9 | ||
|
|
79376a900a | ||
|
|
ca92e7831a | ||
|
|
cc7b0a4381 | ||
|
|
df99f345dc | ||
|
|
a505cb261a | ||
|
|
b8ababa2be | ||
|
|
535ab29deb | ||
|
|
5818ec11bf | ||
|
|
c766c819b3 | ||
|
|
7694b307b8 | ||
|
|
825d520575 | ||
|
|
6838db4647 |
@@ -6,7 +6,7 @@
|
||||
|
||||
cbcfgsdir="config/coreboot"
|
||||
tmpromdel="$XBMK_CACHE/DO_NOT_FLASH"
|
||||
nvmutil="util/nvmutil/nvmutil"
|
||||
nvmutil="util/libreboot-utils/nvmutil"
|
||||
ifdtool="elf/coreboot/default/ifdtool"
|
||||
|
||||
checkvars="CONFIG_GBE_BIN_PATH"
|
||||
@@ -197,8 +197,8 @@ modify_mac()
|
||||
x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "$xbtmp/gbe"
|
||||
|
||||
if [ -n "$new_mac" ] && [ "$new_mac" != "restore" ]; then
|
||||
x_ make -C util/nvmutil clean
|
||||
x_ make -C util/nvmutil
|
||||
x_ make -C util/libreboot-utils clean
|
||||
x_ make -C util/libreboot-utils
|
||||
|
||||
x_ "$nvmutil" "$xbtmp/gbe" setmac "$new_mac"
|
||||
fi
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/nvm
|
||||
/nvmutil
|
||||
/mkhtemp
|
||||
*.bin
|
||||
*.o
|
||||
*.d
|
||||
@@ -24,8 +24,9 @@ STRICT = $(WARN) -std=c90 -pedantic -Werror
|
||||
HELLFLAGS = $(STRICT) -Weverything
|
||||
|
||||
PROG = nvmutil
|
||||
PROGMKH = mkhtemp
|
||||
|
||||
OBJS = \
|
||||
OBJS_NVMUTIL = \
|
||||
obj/nvmutil.o \
|
||||
obj/lib/state.o \
|
||||
obj/lib/file.o \
|
||||
@@ -35,19 +36,33 @@ OBJS = \
|
||||
obj/lib/num.o \
|
||||
obj/lib/io.o \
|
||||
obj/lib/checksum.o \
|
||||
obj/lib/word.o
|
||||
obj/lib/word.o \
|
||||
obj/lib/mkhtemp.o \
|
||||
obj/lib/rand.o
|
||||
|
||||
OBJS_MKHTEMP = \
|
||||
obj/mkhtemp.o \
|
||||
obj/lib/file.o \
|
||||
obj/lib/string.o \
|
||||
obj/lib/num.o \
|
||||
obj/lib/mkhtemp.o \
|
||||
obj/lib/rand.o
|
||||
|
||||
# default mode
|
||||
CFLAGS_MODE = $(PORTABLE)
|
||||
CC_MODE = $(CC)
|
||||
|
||||
all: $(PROG)
|
||||
all: $(PROG) $(PROGMKH)
|
||||
|
||||
$(PROG): $(OBJS)
|
||||
$(CC_MODE) $(OBJS) -o $(PROG) $(LDFLAGS)
|
||||
$(PROG): $(OBJS_NVMUTIL)
|
||||
$(CC_MODE) $(OBJS_NVMUTIL) -o $(PROG) $(LDFLAGS)
|
||||
|
||||
$(PROGMKH): $(OBJS_MKHTEMP)
|
||||
$(CC_MODE) $(OBJS_MKHTEMP) -o $(PROGMKH) $(LDFLAGS)
|
||||
|
||||
# ensure obj directory exists
|
||||
$(OBJS): obj
|
||||
$(OBJS_NVMUTIL): obj
|
||||
$(OBJS_MKHTEMP): obj
|
||||
|
||||
obj:
|
||||
mkdir obj || true
|
||||
@@ -58,6 +73,9 @@ obj:
|
||||
obj/nvmutil.o: nvmutil.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c nvmutil.c -o obj/nvmutil.o
|
||||
|
||||
obj/mkhtemp.o: mkhtemp.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c mkhtemp.c -o obj/mkhtemp.o
|
||||
|
||||
# library/helper objects
|
||||
|
||||
obj/lib/state.o: lib/state.c
|
||||
@@ -87,18 +105,27 @@ obj/lib/checksum.o: lib/checksum.c
|
||||
obj/lib/word.o: lib/word.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c lib/word.c -o obj/lib/word.o
|
||||
|
||||
obj/lib/mkhtemp.o: lib/mkhtemp.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c lib/mkhtemp.c -o obj/lib/mkhtemp.o
|
||||
|
||||
obj/lib/rand.o: lib/rand.c
|
||||
$(CC_MODE) $(CFLAGS_MODE) -c lib/rand.c -o obj/lib/rand.o
|
||||
|
||||
# install
|
||||
|
||||
install: $(PROG)
|
||||
install: $(PROG) $(PROGMKH)
|
||||
$(INSTALL) -d $(DESTDIR)$(PREFIX)/bin
|
||||
$(INSTALL) $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||
$(INSTALL) $(PROGMKH) $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGMKH)
|
||||
|
||||
clean:
|
||||
rm -f $(PROG) $(OBJS)
|
||||
rm -f $(PROG) $(PROGMKH) $(OBJS_NVMUTIL) $(OBJS_MKHTEMP)
|
||||
|
||||
distclean: clean
|
||||
|
||||
@@ -1,31 +1,25 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
|
||||
|
||||
TODO: this file should be split, into headers for each
|
||||
C source file specifically. it was originally just
|
||||
for nvmutil, until i added mkhtemp to the mix
|
||||
*/
|
||||
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* for linux getrandom
|
||||
*/
|
||||
#if defined(__linux__)
|
||||
#include <errno.h>
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<sys/random.h>)
|
||||
#include <sys/random.h>
|
||||
#define HAVE_GETRANDOM 1
|
||||
#endif
|
||||
#endif
|
||||
#if !defined(HAVE_GETRANDOM)
|
||||
#include <sys/syscall.h>
|
||||
#if defined(SYS_getrandom)
|
||||
#define HAVE_GETRANDOM_SYSCALL 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define items(x) (sizeof((x)) / sizeof((x)[0]))
|
||||
@@ -35,11 +29,27 @@
|
||||
|
||||
int fchmod(int fd, mode_t mode);
|
||||
|
||||
/* analog of SSIZE_MAX
|
||||
#define MKHTEMP_RETRY_MAX 512
|
||||
#define MKHTEMP_SPIN_THRESHOLD 32
|
||||
|
||||
#define MKHTEMP_FILE 0
|
||||
#define MKHTEMP_DIR 1
|
||||
|
||||
|
||||
/* if 1: on operations that
|
||||
* check ownership, always
|
||||
* permit root to access even
|
||||
* if not the file/dir owner
|
||||
*/
|
||||
#ifndef ALLOW_ROOT_OVERRIDE
|
||||
#define ALLOW_ROOT_OVERRIDE 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#ifndef X_LONG_MAX
|
||||
#define X_LONG_MAX ((long)(~((long)1 << (sizeof(long)*CHAR_BIT-1))))
|
||||
#ifndef SSIZE_MAX
|
||||
#define SSIZE_MAX ((ssize_t)(~((ssize_t)1 << (sizeof(ssize_t)*CHAR_BIT-1))))
|
||||
#endif
|
||||
|
||||
|
||||
@@ -72,8 +82,8 @@ int fchmod(int fd, mode_t mode);
|
||||
#define MAX_ZERO_RW_RETRY 5
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_REAL_PREAD_PWRITE
|
||||
#define HAVE_REAL_PREAD_PWRITE 0
|
||||
#ifndef REAL_POS_IO
|
||||
#define REAL_POS_IO 0
|
||||
#endif
|
||||
|
||||
#ifndef LOOP_EAGAIN
|
||||
@@ -95,6 +105,10 @@ int fchmod(int fd, mode_t mode);
|
||||
#define EXIT_SUCCESS 0
|
||||
#endif
|
||||
|
||||
#ifndef O_NOCTTY
|
||||
#define O_NOCTTY 0
|
||||
#endif
|
||||
|
||||
#ifndef O_ACCMODE
|
||||
#define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
|
||||
#endif
|
||||
@@ -208,14 +222,14 @@ int fchmod(int fd, mode_t mode);
|
||||
*/
|
||||
|
||||
struct commands {
|
||||
unsigned long chk;
|
||||
size_t chk;
|
||||
char *str;
|
||||
void (*run)(void);
|
||||
int argc;
|
||||
unsigned char arg_part;
|
||||
unsigned char chksum_read;
|
||||
unsigned char chksum_write;
|
||||
unsigned long rw_size; /* within the 4KB GbE part */
|
||||
size_t rw_size; /* within the 4KB GbE part */
|
||||
int flags; /* e.g. O_RDWR or O_RDONLY */
|
||||
};
|
||||
|
||||
@@ -254,7 +268,7 @@ struct xfile {
|
||||
off_t gbe_file_size;
|
||||
off_t gbe_tmp_size;
|
||||
|
||||
unsigned long part;
|
||||
size_t part;
|
||||
unsigned char part_modified[2];
|
||||
unsigned char part_valid[2];
|
||||
|
||||
@@ -262,6 +276,11 @@ struct xfile {
|
||||
unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
|
||||
|
||||
unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
|
||||
|
||||
/* we later rename in-place, using old fd. renameat() */
|
||||
int dirfd;
|
||||
char *base;
|
||||
char *tmpbase;
|
||||
};
|
||||
|
||||
/* Command table, MAC address, files
|
||||
@@ -278,7 +297,7 @@ struct xstate {
|
||||
|
||||
char *argv0;
|
||||
|
||||
unsigned long i; /* index to cmd[] for current command */
|
||||
size_t i; /* index to cmd[] for current command */
|
||||
int no_cmd;
|
||||
|
||||
/* Cat commands set this.
|
||||
@@ -286,28 +305,39 @@ struct xstate {
|
||||
int cat;
|
||||
};
|
||||
|
||||
struct filesystem {
|
||||
int rootfd;
|
||||
};
|
||||
|
||||
|
||||
struct xstate *xstatus(int argc, char *argv[]);
|
||||
struct xstate *xstart(int argc, char *argv[]);
|
||||
struct xstate *xstatus(void);
|
||||
|
||||
/* Sanitize command tables.
|
||||
*/
|
||||
|
||||
void sanitize_command_list(void);
|
||||
void sanitize_command_index(unsigned long c);
|
||||
void sanitize_command_index(size_t c);
|
||||
|
||||
/* Argument handling (user input)
|
||||
*/
|
||||
|
||||
void set_cmd(int argc, char *argv[]);
|
||||
void set_cmd_args(int argc, char *argv[]);
|
||||
unsigned long conv_argv_part_num(const char *part_str);
|
||||
int xstrxcmp(const char *a, const char *b, unsigned long maxlen);
|
||||
size_t conv_argv_part_num(const char *part_str);
|
||||
|
||||
/* Prep files for reading
|
||||
*/
|
||||
|
||||
void open_gbe_file(void);
|
||||
int fd_verify_regular(int fd,
|
||||
const struct stat *expected,
|
||||
struct stat *out);
|
||||
int fd_verify_identity(int fd,
|
||||
const struct stat *expected,
|
||||
struct stat *out);
|
||||
int fd_verify_dir_identity(int fd,
|
||||
const struct stat *expected);
|
||||
int is_owner(struct stat *st);
|
||||
int lock_file(int fd, int flags);
|
||||
int same_file(int fd, struct stat *st_old, int check_size);
|
||||
void xopen(int *fd, const char *path, int flags, struct stat *st);
|
||||
@@ -318,45 +348,50 @@ void xopen(int *fd, const char *path, int flags, struct stat *st);
|
||||
void copy_gbe(void);
|
||||
void read_file(void);
|
||||
void read_checksums(void);
|
||||
int good_checksum(unsigned long partnum);
|
||||
int good_checksum(size_t partnum);
|
||||
|
||||
/* validate commands
|
||||
*/
|
||||
|
||||
void check_command_num(unsigned long c);
|
||||
unsigned char valid_command(unsigned long c);
|
||||
void check_command_num(size_t c);
|
||||
unsigned char valid_command(size_t c);
|
||||
|
||||
/* Helper functions for command: setmac
|
||||
*/
|
||||
|
||||
void cmd_helper_setmac(void);
|
||||
void parse_mac_string(void);
|
||||
unsigned long xstrxlen(const char *scmp, unsigned long maxlen);
|
||||
void set_mac_byte(unsigned long mac_byte_pos);
|
||||
void set_mac_nib(unsigned long mac_str_pos,
|
||||
unsigned long mac_byte_pos, unsigned long mac_nib_pos);
|
||||
void set_mac_byte(size_t mac_byte_pos);
|
||||
void set_mac_nib(size_t mac_str_pos,
|
||||
size_t mac_byte_pos, size_t mac_nib_pos);
|
||||
void write_mac_part(size_t partnum);
|
||||
|
||||
/* string functions
|
||||
*/
|
||||
|
||||
int slen(const char *scmp, size_t maxlen,
|
||||
size_t *rval);
|
||||
int scmp(const char *a, const char *b,
|
||||
size_t maxlen, int *rval);
|
||||
int sdup(const char *s,
|
||||
size_t n, char **dest);
|
||||
int scat(const char *s1, const char *s2,
|
||||
size_t n, char **dest);
|
||||
int dcat(const char *s, size_t n,
|
||||
size_t off, char **dest1,
|
||||
char **dest2);
|
||||
/* numerical functions
|
||||
*/
|
||||
|
||||
unsigned short hextonum(char ch_s);
|
||||
unsigned long rlong(void);
|
||||
#if !(defined(FALLBACK_RAND_1989) && \
|
||||
((FALLBACK_RAND_1989) > 0))
|
||||
#if defined(__linux__)
|
||||
#if defined(HAVE_GETRANDOM) || \
|
||||
defined(HAVE_GETRANDOM_SYSCALL)
|
||||
int fallback_rand_getrandom(void *buf, size_t len);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
unsigned long fallback_rand_1989(void);
|
||||
unsigned long entropy_jitter(void);
|
||||
#endif
|
||||
void write_mac_part(unsigned long partnum);
|
||||
size_t rlong(void);
|
||||
|
||||
/* Helper functions for command: dump
|
||||
*/
|
||||
|
||||
void cmd_helper_dump(void);
|
||||
void print_mac_from_nvm(unsigned long partnum);
|
||||
void hexdump(unsigned long partnum);
|
||||
void print_mac_from_nvm(size_t partnum);
|
||||
void hexdump(size_t partnum);
|
||||
|
||||
/* Helper functions for command: swap
|
||||
*/
|
||||
@@ -375,7 +410,7 @@ void cmd_helper_copy(void);
|
||||
void cmd_helper_cat(void);
|
||||
void cmd_helper_cat16(void);
|
||||
void cmd_helper_cat128(void);
|
||||
void cat(unsigned long nff);
|
||||
void cat(size_t nff);
|
||||
void cat_buf(unsigned char *b);
|
||||
|
||||
/* Command verification/control
|
||||
@@ -388,51 +423,51 @@ void cmd_helper_err(void);
|
||||
*/
|
||||
|
||||
void write_gbe_file(void);
|
||||
void set_checksum(unsigned long part);
|
||||
unsigned short calculated_checksum(unsigned long p);
|
||||
void set_checksum(size_t part);
|
||||
unsigned short calculated_checksum(size_t p);
|
||||
|
||||
/* NVM read/write
|
||||
*/
|
||||
|
||||
unsigned short nvm_word(unsigned long pos16, unsigned long part);
|
||||
void set_nvm_word(unsigned long pos16,
|
||||
unsigned long part, unsigned short val16);
|
||||
void set_part_modified(unsigned long p);
|
||||
void check_nvm_bound(unsigned long pos16, unsigned long part);
|
||||
void check_bin(unsigned long a, const char *a_name);
|
||||
unsigned short nvm_word(size_t pos16, size_t part);
|
||||
void set_nvm_word(size_t pos16,
|
||||
size_t part, unsigned short val16);
|
||||
void set_part_modified(size_t p);
|
||||
void check_nvm_bound(size_t pos16, size_t part);
|
||||
void check_bin(size_t a, const char *a_name);
|
||||
|
||||
/* GbE file read/write
|
||||
*/
|
||||
|
||||
void rw_gbe_file_part(unsigned long p, int rw_type,
|
||||
void rw_gbe_file_part(size_t p, int rw_type,
|
||||
const char *rw_type_str);
|
||||
void write_to_gbe_bin(void);
|
||||
int gbe_mv(void);
|
||||
void check_written_part(unsigned long p);
|
||||
void check_written_part(size_t p);
|
||||
void report_io_err_rw(void);
|
||||
unsigned char *gbe_mem_offset(unsigned long part, const char *f_op);
|
||||
off_t gbe_file_offset(unsigned long part, const char *f_op);
|
||||
off_t gbe_x_offset(unsigned long part, const char *f_op,
|
||||
unsigned char *gbe_mem_offset(size_t part, const char *f_op);
|
||||
off_t gbe_file_offset(size_t part, const char *f_op);
|
||||
off_t gbe_x_offset(size_t part, const char *f_op,
|
||||
const char *d_type, off_t nsize, off_t ncmp);
|
||||
long rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
|
||||
ssize_t rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
|
||||
off_t off, int rw_type);
|
||||
|
||||
/* Generic read/write
|
||||
*/
|
||||
|
||||
int fsync_dir(const char *path);
|
||||
long rw_file_exact(int fd, unsigned char *mem, unsigned long len,
|
||||
ssize_t rw_file_exact(int fd, unsigned char *mem, size_t len,
|
||||
off_t off, int rw_type, int loop_eagain, int loop_eintr,
|
||||
unsigned long max_retries, int off_reset);
|
||||
long prw(int fd, void *mem, unsigned long nrw,
|
||||
size_t max_retries, int off_reset);
|
||||
ssize_t prw(int fd, void *mem, size_t nrw,
|
||||
off_t off, int rw_type, int loop_eagain, int loop_eintr,
|
||||
int off_reset);
|
||||
int io_args(int fd, void *mem, unsigned long nrw,
|
||||
int io_args(int fd, void *mem, size_t nrw,
|
||||
off_t off, int rw_type);
|
||||
int check_file(int fd, struct stat *st);
|
||||
long rw_over_nrw(long r, unsigned long nrw);
|
||||
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
|
||||
HAVE_REAL_PREAD_PWRITE < 1
|
||||
ssize_t rw_over_nrw(ssize_t r, size_t nrw);
|
||||
#if !defined(REAL_POS_IO) || \
|
||||
REAL_POS_IO < 1
|
||||
off_t lseek_on_eintr(int fd, off_t off,
|
||||
int whence, int loop_eagain, int loop_eintr);
|
||||
#endif
|
||||
@@ -442,18 +477,79 @@ int try_err(int loop_err, int errval);
|
||||
*/
|
||||
|
||||
void usage(void);
|
||||
void err(int nvm_errval, const char *msg, ...);
|
||||
void err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...);
|
||||
void b0rk(int nvm_errval, const char *msg, ...);
|
||||
int exit_cleanup(void);
|
||||
const char *getnvmprogname(void);
|
||||
|
||||
/* Portable libc functions
|
||||
void err_mkhtemp(int stfu, int errval, const char *msg, ...);
|
||||
|
||||
/* libc hardening
|
||||
*/
|
||||
|
||||
char *new_tmpfile(int *fd, int local, const char *path);
|
||||
int mkstemp_n(char *template);
|
||||
char *get_tmpdir(void);
|
||||
int new_tmpfile(int *fd, char **path, char *tmpdir,
|
||||
const char *template);
|
||||
int new_tmpdir(int *fd, char **path, char *tmpdir,
|
||||
const char *template);
|
||||
int new_tmp_common(int *fd, char **path, int type,
|
||||
char *tmpdir, const char *template);
|
||||
int mkhtemp_try_create(int dirfd,
|
||||
struct stat *st_dir_initial,
|
||||
char *fname_copy,
|
||||
char *p,
|
||||
size_t xc,
|
||||
int *fd,
|
||||
struct stat *st,
|
||||
int type);
|
||||
int
|
||||
mkhtemp_tmpfile_linux(int dirfd,
|
||||
struct stat *st_dir_initial,
|
||||
char *fname_copy,
|
||||
char *p,
|
||||
size_t xc,
|
||||
int *fd,
|
||||
struct stat *st);
|
||||
int mkhtemp(int *fd, struct stat *st,
|
||||
char *template, int dirfd, const char *fname,
|
||||
struct stat *st_dir_initial, int type);
|
||||
int mkhtemp_fill_random(char *p, size_t xc);
|
||||
int world_writeable_and_sticky(const char *s,
|
||||
int sticky_allowed, int always_sticky);
|
||||
int same_dir(const char *a, const char *b);
|
||||
int tmpdir_policy(const char *path,
|
||||
int *allow_noworld_unsticky);
|
||||
char *env_tmpdir(int always_sticky, char **tmpdir,
|
||||
char *override_tmpdir);
|
||||
int secure_file(int *fd,
|
||||
struct stat *st,
|
||||
struct stat *expected,
|
||||
int bad_flags,
|
||||
int check_seek,
|
||||
int do_lock,
|
||||
mode_t mode);
|
||||
int close_on_eintr(int fd);
|
||||
int fsync_on_eintr(int fd);
|
||||
int fs_rename_at(int olddirfd, const char *old,
|
||||
int newdirfd, const char *new);
|
||||
int fs_open(const char *path, int flags);
|
||||
void close_no_err(int *fd);
|
||||
void free_if_null(char **buf);
|
||||
int close_warn(int *fd, char *s);
|
||||
struct filesystem *rootfs(void);
|
||||
int fs_resolve_at(int dirfd, const char *path, int flags);
|
||||
int fs_next_component(const char **p,
|
||||
char *name, size_t namesz);
|
||||
int fs_open_component(int dirfd, const char *name,
|
||||
int flags, int is_last);
|
||||
int fs_dirname_basename(const char *path,
|
||||
char **dir, char **base, int allow_relative);
|
||||
int openat2p(int dirfd, const char *path,
|
||||
int flags, mode_t mode);
|
||||
int mkdirat_on_eintr(int dirfd,
|
||||
const char *pathname, mode_t mode);
|
||||
int if_err(int condition, int errval);
|
||||
int if_err_sys(int condition);
|
||||
char *lbgetprogname(char *argv0);
|
||||
|
||||
/* asserts */
|
||||
|
||||
@@ -467,16 +563,16 @@ typedef char static_assert_unsigned_short_is_2[
|
||||
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_long_ulong[
|
||||
(sizeof(unsigned long) == sizeof(long)) ? 1 : -1];
|
||||
typedef char static_assert_unsigned_ssize_t_is_4[
|
||||
(sizeof(size_t) >= 4) ? 1 : -1];
|
||||
typedef char static_assert_ssize_t_ussize_t[
|
||||
(sizeof(size_t) == sizeof(ssize_t)) ? 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
|
||||
typedef char assert_unsigned_ssize_t_ptr[
|
||||
(sizeof(size_t) >= sizeof(void *)) ? 1 : -1
|
||||
];
|
||||
|
||||
/*
|
||||
@@ -17,12 +17,12 @@
|
||||
void
|
||||
read_checksums(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long _p;
|
||||
unsigned long _skip_part;
|
||||
size_t _p;
|
||||
size_t _skip_part;
|
||||
|
||||
unsigned char _num_invalid;
|
||||
unsigned char _max_invalid;
|
||||
@@ -59,16 +59,16 @@ read_checksums(void)
|
||||
if (_num_invalid >= _max_invalid) {
|
||||
|
||||
if (_max_invalid == 1)
|
||||
err(ECANCELED, "%s: part %lu has a bad checksum",
|
||||
f->fname, (unsigned long)f->part);
|
||||
b0rk(ECANCELED, "%s: part %lu has a bad checksum",
|
||||
f->fname, (size_t)f->part);
|
||||
|
||||
err(ECANCELED, "%s: No valid checksum found in file",
|
||||
b0rk(ECANCELED, "%s: No valid checksum found in file",
|
||||
f->fname);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
good_checksum(unsigned long partnum)
|
||||
good_checksum(size_t partnum)
|
||||
{
|
||||
unsigned short expected_checksum;
|
||||
unsigned short actual_checksum;
|
||||
@@ -87,16 +87,16 @@ good_checksum(unsigned long partnum)
|
||||
}
|
||||
|
||||
void
|
||||
set_checksum(unsigned long p)
|
||||
set_checksum(size_t p)
|
||||
{
|
||||
check_bin(p, "part number");
|
||||
set_nvm_word(NVM_CHECKSUM_WORD, p, calculated_checksum(p));
|
||||
}
|
||||
|
||||
unsigned short
|
||||
calculated_checksum(unsigned long p)
|
||||
calculated_checksum(size_t p)
|
||||
{
|
||||
unsigned long c;
|
||||
size_t c;
|
||||
unsigned int val16;
|
||||
|
||||
val16 = 0;
|
||||
@@ -21,10 +21,10 @@
|
||||
void
|
||||
sanitize_command_list(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
|
||||
unsigned long c;
|
||||
unsigned long num_commands;
|
||||
size_t c;
|
||||
size_t num_commands;
|
||||
|
||||
num_commands = items(x->cmd);
|
||||
|
||||
@@ -33,37 +33,41 @@ sanitize_command_list(void)
|
||||
}
|
||||
|
||||
void
|
||||
sanitize_command_index(unsigned long c)
|
||||
sanitize_command_index(size_t c)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[c];
|
||||
|
||||
int _flag;
|
||||
unsigned long gbe_rw_size;
|
||||
size_t gbe_rw_size;
|
||||
|
||||
size_t rval;
|
||||
|
||||
check_command_num(c);
|
||||
|
||||
if (cmd->argc < 3)
|
||||
err(EINVAL, "cmd index %lu: argc below 3, %d",
|
||||
(unsigned long)c, cmd->argc);
|
||||
b0rk(EINVAL, "cmd index %lu: argc below 3, %d",
|
||||
(size_t)c, cmd->argc);
|
||||
|
||||
if (cmd->str == NULL)
|
||||
err(EINVAL, "cmd index %lu: NULL str",
|
||||
(unsigned long)c);
|
||||
b0rk(EINVAL, "cmd index %lu: NULL str",
|
||||
(size_t)c);
|
||||
|
||||
if (*cmd->str == '\0')
|
||||
err(EINVAL, "cmd index %lu: empty str",
|
||||
(unsigned long)c);
|
||||
b0rk(EINVAL, "cmd index %lu: empty str",
|
||||
(size_t)c);
|
||||
|
||||
if (xstrxlen(cmd->str, MAX_CMD_LEN + 1) >
|
||||
MAX_CMD_LEN) {
|
||||
err(EINVAL, "cmd index %lu: str too long: %s",
|
||||
(unsigned long)c, cmd->str);
|
||||
if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
|
||||
b0rk(errno, "Could not get command length");
|
||||
|
||||
if (rval > MAX_CMD_LEN) {
|
||||
b0rk(EINVAL, "cmd index %lu: str too long: %s",
|
||||
(size_t)c, cmd->str);
|
||||
}
|
||||
|
||||
if (cmd->run == NULL)
|
||||
err(EINVAL, "cmd index %lu: cmd ptr null",
|
||||
(unsigned long)c);
|
||||
b0rk(EINVAL, "cmd index %lu: cmd ptr null",
|
||||
(size_t)c);
|
||||
|
||||
check_bin(cmd->arg_part, "cmd.arg_part");
|
||||
check_bin(cmd->chksum_read, "cmd.chksum_read");
|
||||
@@ -76,36 +80,40 @@ sanitize_command_index(unsigned long c)
|
||||
case NVM_SIZE:
|
||||
break;
|
||||
default:
|
||||
err(EINVAL, "Unsupported rw_size: %lu",
|
||||
(unsigned long)gbe_rw_size);
|
||||
b0rk(EINVAL, "Unsupported rw_size: %lu",
|
||||
(size_t)gbe_rw_size);
|
||||
}
|
||||
|
||||
if (gbe_rw_size > GBE_PART_SIZE)
|
||||
err(EINVAL, "rw_size larger than GbE part: %lu",
|
||||
(unsigned long)gbe_rw_size);
|
||||
b0rk(EINVAL, "rw_size larger than GbE part: %lu",
|
||||
(size_t)gbe_rw_size);
|
||||
|
||||
_flag = (cmd->flags & O_ACCMODE);
|
||||
|
||||
if (_flag != O_RDONLY &&
|
||||
_flag != O_RDWR)
|
||||
err(EINVAL, "invalid cmd.flags setting");
|
||||
b0rk(EINVAL, "invalid cmd.flags setting");
|
||||
}
|
||||
|
||||
void
|
||||
set_cmd(int argc, char *argv[])
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
const char *cmd;
|
||||
|
||||
unsigned long c;
|
||||
int rval;
|
||||
|
||||
size_t c;
|
||||
|
||||
for (c = 0; c < items(x->cmd); c++) {
|
||||
|
||||
cmd = x->cmd[c].str;
|
||||
|
||||
/* not the right command */
|
||||
if (xstrxcmp(argv[2], cmd, MAX_CMD_LEN) != 0)
|
||||
continue;
|
||||
if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
|
||||
err_no_cleanup(0, EINVAL,
|
||||
"could not compare command strings");
|
||||
if (rval != 0)
|
||||
continue; /* not the right command */
|
||||
|
||||
/* valid command found */
|
||||
if (argc >= x->cmd[c].argc) {
|
||||
@@ -115,18 +123,19 @@ set_cmd(int argc, char *argv[])
|
||||
return;
|
||||
}
|
||||
|
||||
err(EINVAL,
|
||||
err_no_cleanup(0, EINVAL,
|
||||
"Too few args on command '%s'", cmd);
|
||||
}
|
||||
|
||||
|
||||
x->no_cmd = 1;
|
||||
}
|
||||
|
||||
void
|
||||
set_cmd_args(int argc, char *argv[])
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
unsigned long i = x->i;
|
||||
struct xstate *x = xstatus();
|
||||
size_t i = x->i;
|
||||
struct commands *cmd = &x->cmd[i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
@@ -139,11 +148,11 @@ set_cmd_args(int argc, char *argv[])
|
||||
/* Maintainer bug
|
||||
*/
|
||||
if (cmd->arg_part && argc < 4)
|
||||
err(EINVAL,
|
||||
b0rk(EINVAL,
|
||||
"arg_part set for command that needs argc4");
|
||||
|
||||
if (cmd->arg_part && i == CMD_SETMAC)
|
||||
err(EINVAL,
|
||||
b0rk(EINVAL,
|
||||
"arg_part set on CMD_SETMAC");
|
||||
|
||||
if (i == CMD_SETMAC) {
|
||||
@@ -159,35 +168,35 @@ set_cmd_args(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long
|
||||
size_t
|
||||
conv_argv_part_num(const char *part_str)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
if (part_str[0] == '\0' || part_str[1] != '\0')
|
||||
err(EINVAL, "Partnum string '%s' wrong length", part_str);
|
||||
b0rk(EINVAL, "Partnum string '%s' wrong length", part_str);
|
||||
|
||||
/* char signedness is implementation-defined
|
||||
*/
|
||||
ch = (unsigned char)part_str[0];
|
||||
if (ch < '0' || ch > '1')
|
||||
err(EINVAL, "Bad part number (%c)", ch);
|
||||
b0rk(EINVAL, "Bad part number (%c)", ch);
|
||||
|
||||
return (unsigned long)(ch - '0');
|
||||
return (size_t)(ch - '0');
|
||||
}
|
||||
|
||||
void
|
||||
check_command_num(unsigned long c)
|
||||
check_command_num(size_t c)
|
||||
{
|
||||
if (!valid_command(c))
|
||||
err(EINVAL, "Invalid run_cmd arg: %lu",
|
||||
(unsigned long)c);
|
||||
b0rk(EINVAL, "Invalid run_cmd arg: %lu",
|
||||
(size_t)c);
|
||||
}
|
||||
|
||||
unsigned char
|
||||
valid_command(unsigned long c)
|
||||
valid_command(size_t c)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd;
|
||||
|
||||
if (c >= items(x->cmd))
|
||||
@@ -196,7 +205,7 @@ valid_command(unsigned long c)
|
||||
cmd = &x->cmd[c];
|
||||
|
||||
if (c != cmd->chk)
|
||||
err(EINVAL,
|
||||
b0rk(EINVAL,
|
||||
"Invalid cmd chk value (%lu) vs arg: %lu",
|
||||
cmd->chk, c);
|
||||
|
||||
@@ -206,10 +215,10 @@ valid_command(unsigned long c)
|
||||
void
|
||||
cmd_helper_setmac(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct macaddr *mac = &x->mac;
|
||||
|
||||
unsigned long partnum;
|
||||
size_t partnum;
|
||||
|
||||
check_cmd(cmd_helper_setmac, "setmac");
|
||||
|
||||
@@ -223,13 +232,18 @@ cmd_helper_setmac(void)
|
||||
void
|
||||
parse_mac_string(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct macaddr *mac = &x->mac;
|
||||
|
||||
unsigned long mac_byte;
|
||||
size_t mac_byte;
|
||||
|
||||
if (xstrxlen(x->mac.str, 18) != 17)
|
||||
err(EINVAL, "MAC address is the wrong length");
|
||||
size_t rval;
|
||||
|
||||
if (slen(x->mac.str, 18, &rval) < 0)
|
||||
b0rk(EINVAL, "Could not determine MAC length");
|
||||
|
||||
if (rval != 17)
|
||||
b0rk(EINVAL, "MAC address is the wrong length");
|
||||
|
||||
memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
|
||||
|
||||
@@ -237,28 +251,28 @@ parse_mac_string(void)
|
||||
set_mac_byte(mac_byte);
|
||||
|
||||
if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0)
|
||||
err(EINVAL, "Must not specify all-zeroes MAC address");
|
||||
b0rk(EINVAL, "Must not specify all-zeroes MAC address");
|
||||
|
||||
if (mac->mac_buf[0] & 1)
|
||||
err(EINVAL, "Must not specify multicast MAC address");
|
||||
b0rk(EINVAL, "Must not specify multicast MAC address");
|
||||
}
|
||||
|
||||
void
|
||||
set_mac_byte(unsigned long mac_byte_pos)
|
||||
set_mac_byte(size_t mac_byte_pos)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct macaddr *mac = &x->mac;
|
||||
|
||||
char separator;
|
||||
|
||||
unsigned long mac_str_pos;
|
||||
unsigned long mac_nib_pos;
|
||||
size_t mac_str_pos;
|
||||
size_t mac_nib_pos;
|
||||
|
||||
mac_str_pos = mac_byte_pos * 3;
|
||||
|
||||
if (mac_str_pos < 15) {
|
||||
if ((separator = mac->str[mac_str_pos + 2]) != ':')
|
||||
err(EINVAL, "Invalid MAC address separator '%c'",
|
||||
b0rk(EINVAL, "Invalid MAC address separator '%c'",
|
||||
separator);
|
||||
}
|
||||
|
||||
@@ -267,10 +281,10 @@ set_mac_byte(unsigned long mac_byte_pos)
|
||||
}
|
||||
|
||||
void
|
||||
set_mac_nib(unsigned long mac_str_pos,
|
||||
unsigned long mac_byte_pos, unsigned long mac_nib_pos)
|
||||
set_mac_nib(size_t mac_str_pos,
|
||||
size_t mac_byte_pos, size_t mac_nib_pos)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct macaddr *mac = &x->mac;
|
||||
|
||||
char mac_ch;
|
||||
@@ -278,9 +292,13 @@ set_mac_nib(unsigned long mac_str_pos,
|
||||
|
||||
mac_ch = mac->str[mac_str_pos + mac_nib_pos];
|
||||
|
||||
if ((hex_num = hextonum(mac_ch)) > 15)
|
||||
err(EINVAL, "Invalid character '%c'",
|
||||
mac->str[mac_str_pos + mac_nib_pos]);
|
||||
if ((hex_num = hextonum(mac_ch)) > 15) {
|
||||
if (hex_num >= 17)
|
||||
b0rk(EIO, "Randomisation failure");
|
||||
else
|
||||
b0rk(EINVAL, "Invalid character '%c'",
|
||||
mac->str[mac_str_pos + mac_nib_pos]);
|
||||
}
|
||||
|
||||
/* If random, ensure that local/unicast bits are set.
|
||||
*/
|
||||
@@ -298,13 +316,13 @@ set_mac_nib(unsigned long mac_str_pos,
|
||||
}
|
||||
|
||||
void
|
||||
write_mac_part(unsigned long partnum)
|
||||
write_mac_part(size_t partnum)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
struct macaddr *mac = &x->mac;
|
||||
|
||||
unsigned long w;
|
||||
size_t w;
|
||||
|
||||
check_bin(partnum, "part number");
|
||||
if (!f->part_valid[partnum])
|
||||
@@ -314,17 +332,17 @@ write_mac_part(unsigned long partnum)
|
||||
set_nvm_word(w, partnum, mac->mac_buf[w]);
|
||||
|
||||
printf("Wrote MAC address to part %lu: ",
|
||||
(unsigned long)partnum);
|
||||
(size_t)partnum);
|
||||
print_mac_from_nvm(partnum);
|
||||
}
|
||||
|
||||
void
|
||||
cmd_helper_dump(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long p;
|
||||
size_t p;
|
||||
|
||||
check_cmd(cmd_helper_dump, "dump");
|
||||
|
||||
@@ -338,12 +356,12 @@ cmd_helper_dump(void)
|
||||
fprintf(stderr,
|
||||
"BAD checksum %04x in part %lu (expected %04x)\n",
|
||||
nvm_word(NVM_CHECKSUM_WORD, p),
|
||||
(unsigned long)p,
|
||||
(size_t)p,
|
||||
calculated_checksum(p));
|
||||
}
|
||||
|
||||
printf("MAC (part %lu): ",
|
||||
(unsigned long)p);
|
||||
(size_t)p);
|
||||
|
||||
print_mac_from_nvm(p);
|
||||
|
||||
@@ -352,9 +370,9 @@ cmd_helper_dump(void)
|
||||
}
|
||||
|
||||
void
|
||||
print_mac_from_nvm(unsigned long partnum)
|
||||
print_mac_from_nvm(size_t partnum)
|
||||
{
|
||||
unsigned long c;
|
||||
size_t c;
|
||||
unsigned short val16;
|
||||
|
||||
for (c = 0; c < 3; c++) {
|
||||
@@ -373,16 +391,16 @@ print_mac_from_nvm(unsigned long partnum)
|
||||
}
|
||||
|
||||
void
|
||||
hexdump(unsigned long partnum)
|
||||
hexdump(size_t partnum)
|
||||
{
|
||||
unsigned long c;
|
||||
unsigned long row;
|
||||
size_t c;
|
||||
size_t row;
|
||||
unsigned short val16;
|
||||
|
||||
for (row = 0; row < 8; row++) {
|
||||
|
||||
printf("%08lx ",
|
||||
(unsigned long)((unsigned long)row << 4));
|
||||
(size_t)((size_t)row << 4));
|
||||
|
||||
for (c = 0; c < 8; c++) {
|
||||
|
||||
@@ -404,24 +422,24 @@ hexdump(unsigned long partnum)
|
||||
void
|
||||
cmd_helper_swap(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
check_cmd(cmd_helper_swap, "swap");
|
||||
|
||||
memcpy(
|
||||
f->buf + (unsigned long)GBE_WORK_SIZE,
|
||||
f->buf + (size_t)GBE_WORK_SIZE,
|
||||
f->buf,
|
||||
GBE_PART_SIZE);
|
||||
|
||||
memcpy(
|
||||
f->buf,
|
||||
f->buf + (unsigned long)GBE_PART_SIZE,
|
||||
f->buf + (size_t)GBE_PART_SIZE,
|
||||
GBE_PART_SIZE);
|
||||
|
||||
memcpy(
|
||||
f->buf + (unsigned long)GBE_PART_SIZE,
|
||||
f->buf + (unsigned long)GBE_WORK_SIZE,
|
||||
f->buf + (size_t)GBE_PART_SIZE,
|
||||
f->buf + (size_t)GBE_WORK_SIZE,
|
||||
GBE_PART_SIZE);
|
||||
|
||||
set_part_modified(0);
|
||||
@@ -431,14 +449,14 @@ cmd_helper_swap(void)
|
||||
void
|
||||
cmd_helper_copy(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
check_cmd(cmd_helper_copy, "copy");
|
||||
|
||||
memcpy(
|
||||
f->buf + (unsigned long)((f->part ^ 1) * GBE_PART_SIZE),
|
||||
f->buf + (unsigned long)(f->part * GBE_PART_SIZE),
|
||||
f->buf + (size_t)((f->part ^ 1) * GBE_PART_SIZE),
|
||||
f->buf + (size_t)(f->part * GBE_PART_SIZE),
|
||||
GBE_PART_SIZE);
|
||||
|
||||
set_part_modified(f->part ^ 1);
|
||||
@@ -447,7 +465,7 @@ cmd_helper_copy(void)
|
||||
void
|
||||
cmd_helper_cat(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
|
||||
check_cmd(cmd_helper_cat, "cat");
|
||||
|
||||
@@ -458,7 +476,7 @@ cmd_helper_cat(void)
|
||||
void
|
||||
cmd_helper_cat16(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
|
||||
check_cmd(cmd_helper_cat16, "cat16");
|
||||
|
||||
@@ -469,7 +487,7 @@ cmd_helper_cat16(void)
|
||||
void
|
||||
cmd_helper_cat128(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
|
||||
check_cmd(cmd_helper_cat128, "cat128");
|
||||
|
||||
@@ -478,20 +496,20 @@ cmd_helper_cat128(void)
|
||||
}
|
||||
|
||||
void
|
||||
cat(unsigned long nff)
|
||||
cat(size_t nff)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long p;
|
||||
unsigned long ff;
|
||||
size_t p;
|
||||
size_t ff;
|
||||
|
||||
p = 0;
|
||||
ff = 0;
|
||||
|
||||
if ((unsigned long)x->cat != nff) {
|
||||
if ((size_t)x->cat != nff) {
|
||||
|
||||
err(ECANCELED, "erroneous call to cat");
|
||||
b0rk(ECANCELED, "erroneous call to cat");
|
||||
}
|
||||
|
||||
fflush(NULL);
|
||||
@@ -501,7 +519,7 @@ cat(unsigned long nff)
|
||||
for (p = 0; p < 2; p++) {
|
||||
|
||||
cat_buf(f->bufcmp +
|
||||
(unsigned long)(p * (f->gbe_file_size >> 1)));
|
||||
(size_t)(p * (f->gbe_file_size >> 1)));
|
||||
|
||||
for (ff = 0; ff < nff; ff++) {
|
||||
|
||||
@@ -514,22 +532,22 @@ void
|
||||
cat_buf(unsigned char *b)
|
||||
{
|
||||
if (b == NULL)
|
||||
err(errno, "null pointer in cat command");
|
||||
b0rk(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)
|
||||
err(errno, "stdout: cat");
|
||||
b0rk(errno, "stdout: cat");
|
||||
}
|
||||
void
|
||||
check_cmd(void (*fn)(void),
|
||||
const char *name)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
unsigned long i = x->i;
|
||||
struct xstate *x = xstatus();
|
||||
size_t i = x->i;
|
||||
|
||||
if (x->cmd[i].run != fn)
|
||||
err(ECANCELED, "Running %s, but cmd %s is set",
|
||||
b0rk(ECANCELED, "Running %s, but cmd %s is set",
|
||||
name, x->cmd[i].str);
|
||||
|
||||
/* prevent second command
|
||||
@@ -541,6 +559,6 @@ check_cmd(void (*fn)(void),
|
||||
void
|
||||
cmd_helper_err(void)
|
||||
{
|
||||
err(ECANCELED,
|
||||
b0rk(ECANCELED,
|
||||
"Erroneously running command twice");
|
||||
}
|
||||
1065
util/libreboot-utils/lib/file.c
Normal file
1065
util/libreboot-utils/lib/file.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
||||
void
|
||||
open_gbe_file(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
@@ -29,19 +29,19 @@ open_gbe_file(void)
|
||||
|
||||
xopen(&f->gbe_fd, f->fname,
|
||||
cmd->flags | O_BINARY |
|
||||
O_NOFOLLOW | O_CLOEXEC, &f->gbe_st);
|
||||
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st);
|
||||
|
||||
if (f->gbe_st.st_nlink > 1)
|
||||
err(EINVAL,
|
||||
b0rk(EINVAL,
|
||||
"%s: warning: file has multiple (%lu) hard links\n",
|
||||
f->fname, (unsigned long)f->gbe_st.st_nlink);
|
||||
f->fname, (size_t)f->gbe_st.st_nlink);
|
||||
|
||||
if (f->gbe_st.st_nlink == 0)
|
||||
err(EIO, "%s: file unlinked while open", f->fname);
|
||||
b0rk(EIO, "%s: file unlinked while open", f->fname);
|
||||
|
||||
_flags = fcntl(f->gbe_fd, F_GETFL);
|
||||
if (_flags == -1)
|
||||
err(errno, "%s: fcntl(F_GETFL)", f->fname);
|
||||
b0rk(errno, "%s: fcntl(F_GETFL)", f->fname);
|
||||
|
||||
/* O_APPEND allows POSIX write() to ignore
|
||||
* the current write offset and write at EOF,
|
||||
@@ -49,7 +49,7 @@ open_gbe_file(void)
|
||||
*/
|
||||
|
||||
if (_flags & O_APPEND)
|
||||
err(EIO, "%s: O_APPEND flag", f->fname);
|
||||
b0rk(EIO, "%s: O_APPEND flag", f->fname);
|
||||
|
||||
f->gbe_file_size = f->gbe_st.st_size;
|
||||
|
||||
@@ -59,17 +59,17 @@ open_gbe_file(void)
|
||||
case SIZE_128KB:
|
||||
break;
|
||||
default:
|
||||
err(EINVAL, "File size must be 8KB, 16KB or 128KB");
|
||||
b0rk(EINVAL, "File size must be 8KB, 16KB or 128KB");
|
||||
}
|
||||
|
||||
if (lock_file(f->gbe_fd, cmd->flags) == -1)
|
||||
err(errno, "%s: can't lock", f->fname);
|
||||
b0rk(errno, "%s: can't lock", f->fname);
|
||||
}
|
||||
|
||||
void
|
||||
copy_gbe(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
read_file();
|
||||
@@ -77,19 +77,19 @@ copy_gbe(void)
|
||||
if (f->gbe_file_size == SIZE_8KB)
|
||||
return;
|
||||
|
||||
memcpy(f->buf + (unsigned long)GBE_PART_SIZE,
|
||||
f->buf + (unsigned long)(f->gbe_file_size >> 1),
|
||||
(unsigned long)GBE_PART_SIZE);
|
||||
memcpy(f->buf + (size_t)GBE_PART_SIZE,
|
||||
f->buf + (size_t)(f->gbe_file_size >> 1),
|
||||
(size_t)GBE_PART_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
read_file(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
struct stat _st;
|
||||
long _r;
|
||||
ssize_t _r;
|
||||
|
||||
/* read main file
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ read_file(void)
|
||||
MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||
|
||||
if (_r < 0)
|
||||
err(errno, "%s: read failed", f->fname);
|
||||
b0rk(errno, "%s: read failed", f->fname);
|
||||
|
||||
/* copy to tmpfile
|
||||
*/
|
||||
@@ -107,55 +107,55 @@ read_file(void)
|
||||
MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||
|
||||
if (_r < 0)
|
||||
err(errno, "%s: %s: copy failed",
|
||||
b0rk(errno, "%s: %s: copy failed",
|
||||
f->fname, f->tname);
|
||||
|
||||
/* file size comparison
|
||||
*/
|
||||
if (fstat(f->tmp_fd, &_st) == -1)
|
||||
err(errno, "%s: stat", f->tname);
|
||||
b0rk(errno, "%s: stat", f->tname);
|
||||
|
||||
f->gbe_tmp_size = _st.st_size;
|
||||
|
||||
if (f->gbe_tmp_size != f->gbe_file_size)
|
||||
err(EIO, "%s: %s: not the same size",
|
||||
b0rk(EIO, "%s: %s: not the same size",
|
||||
f->fname, f->tname);
|
||||
|
||||
/* needs sync, for verification
|
||||
*/
|
||||
if (fsync_on_eintr(f->tmp_fd) == -1)
|
||||
err(errno, "%s: fsync (tmpfile copy)", f->tname);
|
||||
b0rk(errno, "%s: fsync (tmpfile copy)", f->tname);
|
||||
|
||||
_r = 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)
|
||||
err(errno, "%s: read failed (cmp)", f->tname);
|
||||
b0rk(errno, "%s: read failed (cmp)", f->tname);
|
||||
|
||||
if (memcmp(f->buf, f->bufcmp, f->gbe_file_size) != 0)
|
||||
err(errno, "%s: %s: read contents differ (pre-test)",
|
||||
b0rk(errno, "%s: %s: read contents differ (pre-test)",
|
||||
f->fname, f->tname);
|
||||
}
|
||||
|
||||
void
|
||||
write_gbe_file(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long p;
|
||||
size_t p;
|
||||
unsigned char update_checksum;
|
||||
|
||||
if ((cmd->flags & O_ACCMODE) == O_RDONLY)
|
||||
return;
|
||||
|
||||
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
|
||||
err(errno, "%s: file inode/device changed", f->tname);
|
||||
b0rk(errno, "%s: file inode/device changed", f->tname);
|
||||
|
||||
if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
|
||||
err(errno, "%s: file has changed", f->fname);
|
||||
b0rk(errno, "%s: file has changed", f->fname);
|
||||
|
||||
update_checksum = cmd->chksum_write;
|
||||
|
||||
@@ -171,25 +171,25 @@ write_gbe_file(void)
|
||||
}
|
||||
|
||||
void
|
||||
rw_gbe_file_part(unsigned long p, int rw_type,
|
||||
rw_gbe_file_part(size_t p, int rw_type,
|
||||
const char *rw_type_str)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
long rval;
|
||||
ssize_t rval;
|
||||
|
||||
off_t file_offset;
|
||||
|
||||
unsigned long gbe_rw_size;
|
||||
size_t gbe_rw_size;
|
||||
unsigned char *mem_offset;
|
||||
|
||||
gbe_rw_size = cmd->rw_size;
|
||||
|
||||
if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
|
||||
err(errno, "%s: %s: part %lu: invalid rw_type, %d",
|
||||
f->fname, rw_type_str, (unsigned long)p, rw_type);
|
||||
b0rk(errno, "%s: %s: part %lu: invalid rw_type, %d",
|
||||
f->fname, rw_type_str, (size_t)p, rw_type);
|
||||
|
||||
mem_offset = gbe_mem_offset(p, rw_type_str);
|
||||
file_offset = (off_t)gbe_file_offset(p, rw_type_str);
|
||||
@@ -198,18 +198,18 @@ rw_gbe_file_part(unsigned long p, int rw_type,
|
||||
gbe_rw_size, file_offset, rw_type);
|
||||
|
||||
if (rval == -1)
|
||||
err(errno, "%s: %s: part %lu",
|
||||
f->fname, rw_type_str, (unsigned long)p);
|
||||
b0rk(errno, "%s: %s: part %lu",
|
||||
f->fname, rw_type_str, (size_t)p);
|
||||
|
||||
if ((unsigned long)rval != gbe_rw_size)
|
||||
err(EIO, "%s: partial %s: part %lu",
|
||||
f->fname, rw_type_str, (unsigned long)p);
|
||||
if ((size_t)rval != gbe_rw_size)
|
||||
b0rk(EIO, "%s: partial %s: part %lu",
|
||||
f->fname, rw_type_str, (size_t)p);
|
||||
}
|
||||
|
||||
void
|
||||
write_to_gbe_bin(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
@@ -226,7 +226,7 @@ write_to_gbe_bin(void)
|
||||
*/
|
||||
|
||||
if (fsync_on_eintr(f->tmp_fd) == -1)
|
||||
err(errno, "%s: fsync (pre-verification)",
|
||||
b0rk(errno, "%s: fsync (pre-verification)",
|
||||
f->tname);
|
||||
|
||||
check_written_part(0);
|
||||
@@ -235,19 +235,12 @@ write_to_gbe_bin(void)
|
||||
report_io_err_rw();
|
||||
|
||||
if (f->io_err_gbe)
|
||||
err(EIO, "%s: bad write", f->fname);
|
||||
b0rk(EIO, "%s: bad write", f->fname);
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
if (close_on_eintr(f->tmp_fd) == -1) {
|
||||
fprintf(stderr, "FAIL: %s: close\n", f->tname);
|
||||
f->io_err_gbe_bin = 1;
|
||||
}
|
||||
|
||||
if (close_on_eintr(f->gbe_fd) == -1) {
|
||||
fprintf(stderr, "FAIL: %s: close\n", f->fname);
|
||||
f->io_err_gbe_bin = 1;
|
||||
}
|
||||
f->io_err_gbe_bin |= -close_warn(&f->tmp_fd, f->tname);
|
||||
f->io_err_gbe_bin |= -close_warn(&f->gbe_fd, f->fname);
|
||||
|
||||
errno = saved_errno;
|
||||
|
||||
@@ -273,11 +266,7 @@ write_to_gbe_bin(void)
|
||||
|
||||
/* removed by rename
|
||||
*/
|
||||
|
||||
if (f->tname != NULL)
|
||||
free(f->tname);
|
||||
|
||||
f->tname = NULL;
|
||||
free_if_null(&f->tname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,15 +281,15 @@ write_to_gbe_bin(void)
|
||||
}
|
||||
|
||||
void
|
||||
check_written_part(unsigned long p)
|
||||
check_written_part(size_t p)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
long rval;
|
||||
ssize_t rval;
|
||||
|
||||
unsigned long gbe_rw_size;
|
||||
size_t gbe_rw_size;
|
||||
|
||||
off_t file_offset;
|
||||
unsigned char *mem_offset;
|
||||
@@ -318,17 +307,17 @@ check_written_part(unsigned long p)
|
||||
memset(f->pad, 0xff, sizeof(f->pad));
|
||||
|
||||
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
|
||||
err(errno, "%s: file inode/device changed", f->tname);
|
||||
b0rk(errno, "%s: file inode/device changed", f->tname);
|
||||
|
||||
if (same_file(f->gbe_fd, &f->gbe_st, 1) < 0)
|
||||
err(errno, "%s: file changed during write", f->fname);
|
||||
b0rk(errno, "%s: file changed during write", f->fname);
|
||||
|
||||
rval = rw_gbe_file_exact(f->tmp_fd, f->pad,
|
||||
gbe_rw_size, file_offset, IO_PREAD);
|
||||
|
||||
if (rval == -1)
|
||||
f->rw_check_err_read[p] = f->io_err_gbe = 1;
|
||||
else if ((unsigned long)rval != gbe_rw_size)
|
||||
else if ((size_t)rval != gbe_rw_size)
|
||||
f->rw_check_partial_read[p] = f->io_err_gbe = 1;
|
||||
else if (memcmp(mem_offset, f->pad, gbe_rw_size) != 0)
|
||||
f->rw_check_bad_part[p] = f->io_err_gbe = 1;
|
||||
@@ -359,10 +348,10 @@ check_written_part(unsigned long p)
|
||||
void
|
||||
report_io_err_rw(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long p;
|
||||
size_t p;
|
||||
|
||||
if (!f->io_err_gbe)
|
||||
return;
|
||||
@@ -374,22 +363,22 @@ report_io_err_rw(void)
|
||||
if (f->rw_check_err_read[p])
|
||||
fprintf(stderr,
|
||||
"%s: pread: p%lu (post-verification)\n",
|
||||
f->fname, (unsigned long)p);
|
||||
f->fname, (size_t)p);
|
||||
if (f->rw_check_partial_read[p])
|
||||
fprintf(stderr,
|
||||
"%s: partial pread: p%lu (post-verification)\n",
|
||||
f->fname, (unsigned long)p);
|
||||
f->fname, (size_t)p);
|
||||
if (f->rw_check_bad_part[p])
|
||||
fprintf(stderr,
|
||||
"%s: pwrite: corrupt write on p%lu\n",
|
||||
f->fname, (unsigned long)p);
|
||||
f->fname, (size_t)p);
|
||||
|
||||
if (f->rw_check_err_read[p] ||
|
||||
f->rw_check_partial_read[p]) {
|
||||
fprintf(stderr,
|
||||
"%s: p%lu: skipped checksum verification "
|
||||
"(because read failed)\n",
|
||||
f->fname, (unsigned long)p);
|
||||
f->fname, (size_t)p);
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -402,7 +391,7 @@ report_io_err_rw(void)
|
||||
fprintf(stderr, "BAD");
|
||||
|
||||
fprintf(stderr, " checksum in p%lu on-disk.\n",
|
||||
(unsigned long)p);
|
||||
(size_t)p);
|
||||
|
||||
if (f->post_rw_checksum[p]) {
|
||||
fprintf(stderr,
|
||||
@@ -415,7 +404,7 @@ report_io_err_rw(void)
|
||||
int
|
||||
gbe_mv(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
int rval;
|
||||
@@ -424,7 +413,15 @@ gbe_mv(void)
|
||||
int tmp_gbe_bin_exists;
|
||||
|
||||
char *dest_tmp;
|
||||
int dest_fd;
|
||||
int dest_fd = -1;
|
||||
|
||||
char *dir = NULL;
|
||||
char *base = NULL;
|
||||
char *dest_name = NULL;
|
||||
|
||||
int dirfd = -1;
|
||||
|
||||
struct stat st_dir;
|
||||
|
||||
/* will be set 0 if it doesn't
|
||||
*/
|
||||
@@ -435,91 +432,36 @@ gbe_mv(void)
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
rval = rename(f->tname, f->fname);
|
||||
|
||||
if (rval > -1) {
|
||||
|
||||
/* rename on same filesystem
|
||||
*/
|
||||
rval = fs_rename_at(f->dirfd, f->tmpbase,
|
||||
f->dirfd, f->base);
|
||||
|
||||
if (rval > -1)
|
||||
tmp_gbe_bin_exists = 0;
|
||||
|
||||
if (fsync_dir(f->fname) < 0) {
|
||||
f->io_err_gbe_bin = 1;
|
||||
rval = -1;
|
||||
}
|
||||
|
||||
goto ret_gbe_mv;
|
||||
}
|
||||
|
||||
if (errno != EXDEV)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
/*
|
||||
* OR, cross-filesystem rename:
|
||||
*/
|
||||
|
||||
if ((rval = f->tmp_fd = open(f->tname,
|
||||
O_RDONLY | O_BINARY)) == -1)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
/* create replacement temp in target directory
|
||||
*/
|
||||
dest_tmp = new_tmpfile(&dest_fd, 1, f->fname);
|
||||
if (dest_tmp == NULL)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
/* copy data
|
||||
*/
|
||||
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 (rval < 0)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
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 (rval < 0)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
if (fsync_on_eintr(dest_fd) == -1)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
if (close_on_eintr(dest_fd) == -1)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
if (rename(dest_tmp, f->fname) == -1)
|
||||
goto ret_gbe_mv;
|
||||
|
||||
if (fsync_dir(f->fname) < 0) {
|
||||
f->io_err_gbe_bin = 1;
|
||||
goto ret_gbe_mv;
|
||||
}
|
||||
|
||||
free(dest_tmp);
|
||||
dest_tmp = NULL;
|
||||
|
||||
ret_gbe_mv:
|
||||
|
||||
/* TODO: this whole section is bloat.
|
||||
it can be generalised
|
||||
*/
|
||||
|
||||
if (f->gbe_fd > -1) {
|
||||
if (close_on_eintr(f->gbe_fd) < 0)
|
||||
if (close_on_eintr(f->gbe_fd) < 0) {
|
||||
f->gbe_fd = -1;
|
||||
rval = -1;
|
||||
}
|
||||
f->gbe_fd = -1;
|
||||
|
||||
if (fsync_dir(f->fname) < 0) {
|
||||
f->io_err_gbe_bin = 1;
|
||||
rval = -1;
|
||||
}
|
||||
f->gbe_fd = -1;
|
||||
}
|
||||
|
||||
if (f->tmp_fd > -1) {
|
||||
if (close_on_eintr(f->tmp_fd) < 0)
|
||||
if (close_on_eintr(f->tmp_fd) < 0) {
|
||||
f->tmp_fd = -1;
|
||||
rval = -1;
|
||||
|
||||
}
|
||||
f->tmp_fd = -1;
|
||||
}
|
||||
|
||||
@@ -552,9 +494,9 @@ ret_gbe_mv:
|
||||
* and it is *also* used during file I/O.
|
||||
*/
|
||||
unsigned char *
|
||||
gbe_mem_offset(unsigned long p, const char *f_op)
|
||||
gbe_mem_offset(size_t p, const char *f_op)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
off_t gbe_off;
|
||||
@@ -563,7 +505,7 @@ gbe_mem_offset(unsigned long p, const char *f_op)
|
||||
GBE_PART_SIZE, GBE_WORK_SIZE);
|
||||
|
||||
return (unsigned char *)
|
||||
(f->buf + (unsigned long)gbe_off);
|
||||
(f->buf + (size_t)gbe_off);
|
||||
}
|
||||
|
||||
/* I/O operations filtered here. These operations must
|
||||
@@ -571,9 +513,9 @@ gbe_mem_offset(unsigned long p, const char *f_op)
|
||||
* within the GbE file, and write 4KB of data.
|
||||
*/
|
||||
off_t
|
||||
gbe_file_offset(unsigned long p, const char *f_op)
|
||||
gbe_file_offset(size_t p, const char *f_op)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
off_t gbe_file_half_size;
|
||||
@@ -585,10 +527,10 @@ gbe_file_offset(unsigned long p, const char *f_op)
|
||||
}
|
||||
|
||||
off_t
|
||||
gbe_x_offset(unsigned long p, const char *f_op, const char *d_type,
|
||||
gbe_x_offset(size_t p, const char *f_op, const char *d_type,
|
||||
off_t nsize, off_t ncmp)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
off_t off;
|
||||
@@ -598,24 +540,24 @@ gbe_x_offset(unsigned long p, const char *f_op, const char *d_type,
|
||||
off = ((off_t)p) * (off_t)nsize;
|
||||
|
||||
if (off > ncmp - GBE_PART_SIZE)
|
||||
err(ECANCELED, "%s: GbE %s %s out of bounds",
|
||||
b0rk(ECANCELED, "%s: GbE %s %s out of bounds",
|
||||
f->fname, d_type, f_op);
|
||||
|
||||
if (off != 0 && off != ncmp >> 1)
|
||||
err(ECANCELED, "%s: GbE %s %s at bad offset",
|
||||
b0rk(ECANCELED, "%s: GbE %s %s at bad offset",
|
||||
f->fname, d_type, f_op);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
long
|
||||
rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
|
||||
ssize_t
|
||||
rw_gbe_file_exact(int fd, unsigned char *mem, size_t nrw,
|
||||
off_t off, int rw_type)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
long r;
|
||||
ssize_t r;
|
||||
|
||||
if (io_args(fd, mem, nrw, off, rw_type) == -1)
|
||||
return -1;
|
||||
@@ -624,17 +566,17 @@ rw_gbe_file_exact(int fd, unsigned char *mem, unsigned long nrw,
|
||||
if (mem < f->buf)
|
||||
goto err_rw_gbe_file_exact;
|
||||
|
||||
if ((unsigned long)(mem - f->buf) >= GBE_WORK_SIZE)
|
||||
if ((size_t)(mem - f->buf) >= GBE_WORK_SIZE)
|
||||
goto err_rw_gbe_file_exact;
|
||||
}
|
||||
|
||||
if (off < 0 || off >= f->gbe_file_size)
|
||||
goto err_rw_gbe_file_exact;
|
||||
|
||||
if (nrw > (unsigned long)(f->gbe_file_size - off))
|
||||
if (nrw > (size_t)(f->gbe_file_size - off))
|
||||
goto err_rw_gbe_file_exact;
|
||||
|
||||
if (nrw > (unsigned long)GBE_PART_SIZE)
|
||||
if (nrw > (size_t)GBE_PART_SIZE)
|
||||
goto err_rw_gbe_file_exact;
|
||||
|
||||
r = rw_file_exact(fd, mem, nrw, off, rw_type,
|
||||
1103
util/libreboot-utils/lib/mkhtemp.c
Normal file
1103
util/libreboot-utils/lib/mkhtemp.c
Normal file
File diff suppressed because it is too large
Load Diff
119
util/libreboot-utils/lib/num.c
Normal file
119
util/libreboot-utils/lib/num.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* Numerical functions.
|
||||
* NOTE: randomness was moved to rand.c
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO: properly handle errno in this file
|
||||
*/
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__APPLE__))
|
||||
#include <fcntl.h> /* if not arc4random: /dev/urandom */
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
/* TODO:
|
||||
* make this and errno handling more
|
||||
* flexible
|
||||
|
||||
in particular:
|
||||
hextonum could be modified to
|
||||
write into a buffer instead,
|
||||
with the converted numbers,
|
||||
of an arbitrary length
|
||||
*/
|
||||
unsigned short
|
||||
hextonum(char ch_s)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
|
||||
/* rlong() can return error,
|
||||
but preserves errno if no
|
||||
error. we need to detect
|
||||
this because it handles
|
||||
/dev/urandom sometimes
|
||||
|
||||
therefore, if it's zero
|
||||
at start, we know if there
|
||||
was an err at the end, by
|
||||
return value zero, if errno
|
||||
was set; this is technically
|
||||
valid, since zero is also
|
||||
a valid random number!
|
||||
|
||||
it's an edge case that i had
|
||||
to fix. i'll rewrite the code
|
||||
better later. for now, it
|
||||
should be ok.
|
||||
*/
|
||||
errno = 0;
|
||||
|
||||
unsigned char ch;
|
||||
size_t rval;
|
||||
|
||||
ch = (unsigned char)ch_s;
|
||||
|
||||
if ((unsigned int)(ch - '0') <= 9) {
|
||||
|
||||
rval = ch - '0';
|
||||
goto hextonum_success;
|
||||
}
|
||||
|
||||
ch |= 0x20;
|
||||
|
||||
if ((unsigned int)(ch - 'a') <= 5) {
|
||||
|
||||
rval = ch - 'a' + 10;
|
||||
goto hextonum_success;
|
||||
}
|
||||
|
||||
if (ch == '?' || ch == 'x') {
|
||||
|
||||
rval = rlong();
|
||||
if (errno > 0)
|
||||
goto err_hextonum;
|
||||
|
||||
goto hextonum_success;
|
||||
}
|
||||
|
||||
goto err_hextonum;
|
||||
|
||||
hextonum_success:
|
||||
|
||||
errno = saved_errno;
|
||||
return (unsigned short)rval & 0xf;
|
||||
|
||||
err_hextonum:
|
||||
|
||||
if (errno == saved_errno)
|
||||
errno = EINVAL;
|
||||
else
|
||||
return 17; /* 17 indicates getrandom/urandom fail */
|
||||
|
||||
return 16; /* invalid character */
|
||||
|
||||
/* caller just checks >15. */
|
||||
}
|
||||
|
||||
void
|
||||
check_bin(size_t a, const char *a_name)
|
||||
{
|
||||
if (a > 1)
|
||||
err_no_cleanup(0, EINVAL, "%s must be 0 or 1, but is %lu",
|
||||
a_name, (size_t)a);
|
||||
}
|
||||
114
util/libreboot-utils/lib/rand.c
Normal file
114
util/libreboot-utils/lib/rand.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* Random number generation
|
||||
*/
|
||||
|
||||
#ifndef RAND_H
|
||||
#define RAND_H
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <errno.h>
|
||||
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__APPLE__))
|
||||
#include <fcntl.h> /* if not arc4random: /dev/urandom */
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
/* Random numbers
|
||||
*/
|
||||
|
||||
/* when calling this: save errno
|
||||
* first, then set errno to zero.
|
||||
* on error, this function will
|
||||
* set errno and possibly return
|
||||
*
|
||||
* rlong also preserves errno
|
||||
* and leaves it unchanged on
|
||||
* success, so if you do it
|
||||
* right, you can detect error.
|
||||
* this is because it uses
|
||||
* /dev/urandom which can err.
|
||||
* ditto getrandom (EINTR),
|
||||
* theoretically.
|
||||
*/
|
||||
|
||||
/* for the linux version: we use only the
|
||||
* syscall, because we cannot trust /dev/urandom
|
||||
* to be as robust, and some libc implementations
|
||||
* may default to /dev/urandom under fault conditions.
|
||||
*
|
||||
* for general high reliability, we must abort on
|
||||
* failure. in practise, it will likely never fail.
|
||||
* the arc4random call on bsd never returns error.
|
||||
*/
|
||||
|
||||
size_t
|
||||
rlong(void)
|
||||
{
|
||||
size_t rval;
|
||||
int saved_errno = errno;
|
||||
errno = 0;
|
||||
|
||||
#if (defined(__OpenBSD__) || defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__APPLE__) || \
|
||||
defined(__DragonFly__))
|
||||
|
||||
arc4random_buf(&rval, sizeof(size_t));
|
||||
goto out;
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
size_t off = 0;
|
||||
size_t len = sizeof(rval);
|
||||
ssize_t rc;
|
||||
|
||||
retry_rand:
|
||||
rc = (ssize_t)syscall(SYS_getrandom,
|
||||
(char *)&rval + off, len - off, 0);
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
usleep(100);
|
||||
goto retry_rand;
|
||||
}
|
||||
|
||||
goto err; /* possibly unsupported by kernel */
|
||||
}
|
||||
|
||||
if ((off += (size_t)rc) < len)
|
||||
goto retry_rand;
|
||||
|
||||
goto out;
|
||||
err:
|
||||
/*
|
||||
* getrandom can return with error, butt arc4random
|
||||
* doesn't. generally, getrandom will be reliably,
|
||||
* but we of course have to maintain parity with
|
||||
* BSD. So a rand failure is to be interpreted as
|
||||
* a major systems failure, and we act accordingly.
|
||||
*/
|
||||
err_no_cleanup(1, ECANCELED,
|
||||
"Randomisation failure, possibly unsupported in your kernel.");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
#else
|
||||
#error Unsupported operating system (possibly unsecure randomisation)
|
||||
#endif
|
||||
|
||||
out:
|
||||
errno = saved_errno;
|
||||
return rval;
|
||||
}
|
||||
#endif
|
||||
@@ -23,11 +23,19 @@
|
||||
#include "../include/common.h"
|
||||
|
||||
struct xstate *
|
||||
xstatus(int argc, char *argv[])
|
||||
xstart(int argc, char *argv[])
|
||||
{
|
||||
static int first_run = 1;
|
||||
static char *dir = NULL;
|
||||
static char *base = NULL;
|
||||
char *realdir = NULL;
|
||||
char *tmpdir = NULL;
|
||||
char *tmpbase_local = NULL;
|
||||
|
||||
static struct xstate us = {
|
||||
/* DO NOT MESS THIS UP, OR THERE WILL BE DEMONS */
|
||||
{
|
||||
/* be careful when modifying xstate. you
|
||||
* must set everything precisely */
|
||||
{
|
||||
CMD_DUMP, "dump", cmd_helper_dump, ARGC_3,
|
||||
ARG_NOPART,
|
||||
@@ -86,92 +94,60 @@ xstatus(int argc, char *argv[])
|
||||
|
||||
};
|
||||
|
||||
static int first_run = 1;
|
||||
|
||||
if (!first_run)
|
||||
return &us;
|
||||
|
||||
if (argc < 3)
|
||||
err_no_cleanup(0, EINVAL, "xstart: Too few arguments");
|
||||
if (argv == NULL)
|
||||
err_no_cleanup(0, EINVAL, "xstart: NULL argv");
|
||||
|
||||
first_run = 0;
|
||||
|
||||
us.f.buf = us.f.real_buf;
|
||||
|
||||
first_run = 0;
|
||||
us.argv0 = argv[0];
|
||||
us.f.fname = argv[1];
|
||||
|
||||
if (argc > 1)
|
||||
us.f.fname = argv[1];
|
||||
us.f.tmp_fd = -1;
|
||||
us.f.tname = NULL;
|
||||
|
||||
if (argc < 3)
|
||||
usage();
|
||||
if ((realdir = realpath(us.f.fname, NULL)) == NULL)
|
||||
err_no_cleanup(0, errno, "xstart: can't get realpath of %s",
|
||||
us.f.fname);
|
||||
|
||||
/* https://man.openbsd.org/pledge.2
|
||||
https://man.openbsd.org/unveil.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 604
|
||||
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
|
||||
err(errno, "pledge plus unveil");
|
||||
#elif (OpenBSD) >= 509
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
err(errno, "pledge");
|
||||
#endif
|
||||
#endif
|
||||
if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
|
||||
err_no_cleanup(0, errno, "xstart: don't know CWD of %s",
|
||||
us.f.fname);
|
||||
|
||||
#ifndef S_ISREG
|
||||
err(ECANCELED, "Can't determine file types (S_ISREG undefined)");
|
||||
#endif
|
||||
if ((us.f.base = strdup(base)) == NULL)
|
||||
err_no_cleanup(0, errno, "strdup base");
|
||||
|
||||
#ifndef CHAR_BIT
|
||||
err(ECANCELED, "Unknown char size");
|
||||
#else
|
||||
if (CHAR_BIT != 8)
|
||||
err(EINVAL, "Unsupported char size");
|
||||
#endif
|
||||
us.f.dirfd = fs_open(dir,
|
||||
O_RDONLY | O_DIRECTORY);
|
||||
if (us.f.dirfd < 0)
|
||||
err_no_cleanup(0, errno, "%s: open dir", dir);
|
||||
|
||||
if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir, ".gbe.XXXXXXXXXX") < 0)
|
||||
err_no_cleanup(0, errno, "%s", us.f.tname);
|
||||
|
||||
if (fs_dirname_basename(us.f.tname,
|
||||
&tmpdir, &tmpbase_local, 0) < 0)
|
||||
err_no_cleanup(0, errno, "tmp basename");
|
||||
|
||||
us.f.tmpbase = strdup(tmpbase_local);
|
||||
if (us.f.tmpbase == NULL)
|
||||
err_no_cleanup(0, errno, "strdup tmpbase");
|
||||
|
||||
free_if_null(&tmpdir);
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD) && \
|
||||
(OpenBSD) >= 604
|
||||
/* can only use local tmp on openbsd, due to unveil */
|
||||
us.f.tname = new_tmpfile(&us.f.tmp_fd, 1, NULL);
|
||||
#else
|
||||
us.f.tname = new_tmpfile(&us.f.tmp_fd, 0, NULL);
|
||||
#endif
|
||||
if (us.f.tname == NULL)
|
||||
err(errno, "Can't create tmpfile");
|
||||
err_no_cleanup(0, errno, "x->f.tname null");
|
||||
if (*us.f.tname == '\0')
|
||||
err(errno, "tmp dir is an empty string");
|
||||
err_no_cleanup(0, errno, "x->f.tname empty");
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD) && \
|
||||
OpenBSD >= 604
|
||||
if (unveil(us.f.tname, "rwc") == -1)
|
||||
err(errno, "unveil rwc: %s", us.f.tname);
|
||||
#endif
|
||||
if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
|
||||
err(errno, "%s: stat", us.f.tname);
|
||||
|
||||
sanitize_command_list();
|
||||
|
||||
/* parse user command */
|
||||
set_cmd(argc, argv);
|
||||
set_cmd_args(argc, argv);
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD) && \
|
||||
(OpenBSD) >= 604
|
||||
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
|
||||
if (unveil(us.f.fname, "r") == -1)
|
||||
err(errno, "%s: unveil r", us.f.fname);
|
||||
} else {
|
||||
if (unveil(us.f.fname, "rwc") == -1)
|
||||
err(errno, "%s: unveil rw", us.f.fname);
|
||||
}
|
||||
|
||||
if (unveil(us.f.tname, "rwc") == -1)
|
||||
err(errno, "%s: unveil rwc", us.f.tname);
|
||||
|
||||
if (unveil(NULL, NULL) == -1)
|
||||
err(errno, "unveil block (rw)");
|
||||
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
err(errno, "pledge (kill unveil)");
|
||||
#endif
|
||||
|
||||
open_gbe_file();
|
||||
err_no_cleanup(0, errno, "%s: stat", us.f.tname);
|
||||
|
||||
memset(us.f.real_buf, 0, sizeof(us.f.real_buf));
|
||||
memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp));
|
||||
@@ -179,16 +155,24 @@ xstatus(int argc, char *argv[])
|
||||
/* for good measure */
|
||||
memset(us.f.pad, 0, sizeof(us.f.pad));
|
||||
|
||||
copy_gbe();
|
||||
read_checksums();
|
||||
|
||||
return &us;
|
||||
}
|
||||
|
||||
void
|
||||
err(int nvm_errval, const char *msg, ...)
|
||||
struct xstate *
|
||||
xstatus(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstart(0, NULL);
|
||||
|
||||
if (x == NULL)
|
||||
err_no_cleanup(0, EACCES, "NULL pointer to xstate");
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void
|
||||
b0rk(int nvm_errval, const char *msg, ...)
|
||||
{
|
||||
struct xstate *x = xstatus();
|
||||
|
||||
va_list args;
|
||||
|
||||
@@ -211,35 +195,10 @@ err(int nvm_errval, const char *msg, ...)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *
|
||||
getnvmprogname(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
|
||||
const char *p;
|
||||
static char fallback[] = "nvmutil";
|
||||
|
||||
char *rval = fallback;
|
||||
|
||||
if (x != NULL) {
|
||||
if (x->argv0 == NULL || *x->argv0 == '\0')
|
||||
return "";
|
||||
|
||||
rval = x->argv0;
|
||||
}
|
||||
|
||||
p = strrchr(rval, '/');
|
||||
|
||||
if (p)
|
||||
return p + 1;
|
||||
else
|
||||
return rval;
|
||||
}
|
||||
|
||||
int
|
||||
exit_cleanup(void)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f;
|
||||
|
||||
int close_err;
|
||||
@@ -251,23 +210,17 @@ exit_cleanup(void)
|
||||
if (x != NULL) {
|
||||
f = &x->f;
|
||||
|
||||
if (f->gbe_fd > -1) {
|
||||
if (close_on_eintr(f->gbe_fd) == -1)
|
||||
close_err = 1;
|
||||
f->gbe_fd = -1;
|
||||
}
|
||||
close_no_err(&f->gbe_fd);
|
||||
close_no_err(&f->tmp_fd);
|
||||
close_no_err(&f->tmp_fd);
|
||||
|
||||
if (f->tmp_fd > -1) {
|
||||
if (close_on_eintr(f->tmp_fd) == -1)
|
||||
close_err = 1;
|
||||
}
|
||||
|
||||
if (f->tname != NULL) {
|
||||
if (f->tname != NULL)
|
||||
if (unlink(f->tname) == -1)
|
||||
close_err = 1;
|
||||
}
|
||||
|
||||
f->tmp_fd = -1;
|
||||
close_no_err(&f->dirfd);
|
||||
free_if_null(&f->base);
|
||||
free_if_null(&f->tmpbase);
|
||||
}
|
||||
|
||||
if (saved_errno)
|
||||
284
util/libreboot-utils/lib/string.c
Normal file
284
util/libreboot-utils/lib/string.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* String functions
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
/* strict strcmp */
|
||||
int
|
||||
scmp(const char *a,
|
||||
const char *b,
|
||||
size_t maxlen,
|
||||
int *rval)
|
||||
{
|
||||
size_t ch;
|
||||
unsigned char ac;
|
||||
unsigned char bc;
|
||||
|
||||
if (a == NULL ||
|
||||
b == NULL ||
|
||||
rval == NULL) {
|
||||
errno = EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (ch = 0; ch < maxlen; ch++) {
|
||||
|
||||
ac = (unsigned char)a[ch];
|
||||
bc = (unsigned char)b[ch];
|
||||
|
||||
if (ac != bc) {
|
||||
*rval = ac - bc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ac == '\0') {
|
||||
*rval = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
errno = EFAULT;
|
||||
if (rval != NULL)
|
||||
*rval = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* strict strlen */
|
||||
int
|
||||
slen(const char *s,
|
||||
size_t maxlen,
|
||||
size_t *rval)
|
||||
{
|
||||
size_t ch;
|
||||
|
||||
if (s == NULL ||
|
||||
rval == NULL) {
|
||||
errno = EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (ch = 0;
|
||||
ch < maxlen && s[ch] != '\0';
|
||||
ch++);
|
||||
|
||||
if (ch == maxlen) {
|
||||
/* unterminated */
|
||||
errno = EFAULT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
*rval = ch;
|
||||
return 0;
|
||||
err:
|
||||
if (rval != NULL)
|
||||
*rval = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* strict strdup */
|
||||
int
|
||||
sdup(const char *s,
|
||||
size_t n, char **dest)
|
||||
{
|
||||
size_t size;
|
||||
char *rval;
|
||||
|
||||
if (dest == NULL ||
|
||||
slen(s, n, &size) < 0 ||
|
||||
if_err(size == SIZE_MAX, EOVERFLOW) ||
|
||||
(rval = malloc(size + 1)) == NULL) {
|
||||
|
||||
if (dest != NULL)
|
||||
*dest = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(rval, s, size);
|
||||
*(rval + size) = '\0';
|
||||
|
||||
*dest = rval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* strict strcat */
|
||||
int
|
||||
scat(const char *s1, const char *s2,
|
||||
size_t n, char **dest)
|
||||
{
|
||||
size_t size1;
|
||||
size_t size2;
|
||||
char *rval;
|
||||
|
||||
if (dest == NULL ||
|
||||
slen(s1, n, &size1) < 0 ||
|
||||
slen(s2, n, &size2) < 0 ||
|
||||
if_err(size1 > SIZE_MAX - size2 - 1, EOVERFLOW) ||
|
||||
(rval = malloc(size1 + size2 + 1)) == NULL) {
|
||||
|
||||
if (dest != NULL)
|
||||
*dest = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(rval, s1, size1);
|
||||
memcpy(rval + size1, s2, size2);
|
||||
*(rval + size1 + size2) = '\0';
|
||||
|
||||
*dest = rval;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* strict split/de-cat - off is where
|
||||
2nd buffer will start from */
|
||||
int
|
||||
dcat(const char *s, size_t n,
|
||||
size_t off, char **dest1,
|
||||
char **dest2)
|
||||
{
|
||||
size_t size;
|
||||
char *rval1 = NULL;
|
||||
char *rval2 = NULL;
|
||||
|
||||
if (dest1 == NULL || dest2 == NULL ||
|
||||
slen(s, n, &size) < 0 ||
|
||||
if_err(size == SIZE_MAX, EOVERFLOW) ||
|
||||
if_err(off >= size, EOVERFLOW) ||
|
||||
(rval1 = malloc(off + 1)) == NULL ||
|
||||
(rval2 = malloc(size - off + 1)) == NULL) {
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
memcpy(rval1, s, off);
|
||||
*(rval1 + off) = '\0';
|
||||
|
||||
memcpy(rval2, s + off, size - off);
|
||||
*(rval2 + size - off) = '\0';
|
||||
|
||||
*dest1 = rval1;
|
||||
*dest2 = rval2;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (rval1 != NULL)
|
||||
free(rval1);
|
||||
if (rval2 != NULL)
|
||||
free(rval2);
|
||||
|
||||
if (dest1 != NULL)
|
||||
*dest1 = NULL;
|
||||
if (dest2 != NULL)
|
||||
*dest2 = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* the one for nvmutil state is in state.c */
|
||||
/* this one just exits */
|
||||
void
|
||||
err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
int saved_errno = errno;
|
||||
const char *p;
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio", NULL) == -1)
|
||||
fprintf(stderr, "pledge failure during exit");
|
||||
#endif
|
||||
#endif
|
||||
if (!errno)
|
||||
saved_errno = errno = ECANCELED;
|
||||
|
||||
if ((p = getnvmprogname()) != NULL)
|
||||
fprintf(stderr, "%s: ", p);
|
||||
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
|
||||
if (p != NULL)
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
else
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *
|
||||
getnvmprogname(void)
|
||||
{
|
||||
static char *rval = NULL;
|
||||
static char *p;
|
||||
static int setname = 0;
|
||||
|
||||
if (!setname) {
|
||||
if ((rval = lbgetprogname(NULL)) == NULL)
|
||||
return NULL;
|
||||
|
||||
p = strrchr(rval, '/');
|
||||
if (p)
|
||||
rval = p + 1;
|
||||
|
||||
setname = 1;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* singleton. if string not null,
|
||||
sets the string. after set,
|
||||
will not set anymore. either
|
||||
way, returns the string
|
||||
*/
|
||||
char *
|
||||
lbgetprogname(char *argv0)
|
||||
{
|
||||
static int setname = 0;
|
||||
static char *progname = NULL;
|
||||
size_t len;
|
||||
|
||||
if (!setname) {
|
||||
if (if_err(argv0 == NULL || *argv0 == '\0', EFAULT) ||
|
||||
slen(argv0, 4096, &len) < 0 ||
|
||||
(progname = malloc(len + 1)) == NULL)
|
||||
return NULL;
|
||||
|
||||
memcpy(progname, argv0, len + 1);
|
||||
setname = 1;
|
||||
}
|
||||
|
||||
return progname;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,5 +26,5 @@ usage(void)
|
||||
util, util, util, util,
|
||||
util, util, util);
|
||||
|
||||
err(EINVAL, "Too few arguments");
|
||||
b0rk(EINVAL, "Too few arguments");
|
||||
}
|
||||
@@ -13,12 +13,12 @@
|
||||
#include "../include/common.h"
|
||||
|
||||
unsigned short
|
||||
nvm_word(unsigned long pos16, unsigned long p)
|
||||
nvm_word(size_t pos16, size_t p)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long pos;
|
||||
size_t pos;
|
||||
|
||||
check_nvm_bound(pos16, p);
|
||||
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
|
||||
@@ -28,12 +28,12 @@ nvm_word(unsigned long pos16, unsigned long p)
|
||||
}
|
||||
|
||||
void
|
||||
set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16)
|
||||
set_nvm_word(size_t pos16, size_t p, unsigned short val16)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long pos;
|
||||
size_t pos;
|
||||
|
||||
check_nvm_bound(pos16, p);
|
||||
pos = (pos16 << 1) + (p * GBE_PART_SIZE);
|
||||
@@ -45,9 +45,9 @@ set_nvm_word(unsigned long pos16, unsigned long p, unsigned short val16)
|
||||
}
|
||||
|
||||
void
|
||||
set_part_modified(unsigned long p)
|
||||
set_part_modified(size_t p)
|
||||
{
|
||||
struct xstate *x = xstatus(0, NULL);
|
||||
struct xstate *x = xstatus();
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
check_bin(p, "part number");
|
||||
@@ -55,7 +55,7 @@ set_part_modified(unsigned long p)
|
||||
}
|
||||
|
||||
void
|
||||
check_nvm_bound(unsigned long c, unsigned long p)
|
||||
check_nvm_bound(size_t c, size_t p)
|
||||
{
|
||||
/* Block out of bound NVM access
|
||||
*/
|
||||
@@ -63,6 +63,6 @@ check_nvm_bound(unsigned long c, unsigned long p)
|
||||
check_bin(p, "part number");
|
||||
|
||||
if (c >= NVM_WORDS)
|
||||
err(ECANCELED, "check_nvm_bound: out of bounds %lu",
|
||||
(unsigned long)c);
|
||||
b0rk(ECANCELED, "check_nvm_bound: out of bounds %lu",
|
||||
(size_t)c);
|
||||
}
|
||||
211
util/libreboot-utils/mkhtemp.c
Normal file
211
util/libreboot-utils/mkhtemp.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* WORK IN PROGRESS (proof of concept), or, v0.0000001
|
||||
*
|
||||
* Mkhtemp - Hardened mktemp. Create files and directories
|
||||
* randomly as determined by user's TMPDIR, or fallback. It
|
||||
* attemps to provide mitigation against several TOCTOU-based
|
||||
* attacks e.g. directory rename / symlink attacks, and it
|
||||
* generally provides much higher strictness than previous
|
||||
* implementations such as mktemp, mkstemp or even mkdtemp.
|
||||
*
|
||||
* It uses several modern features by default, e.g. openat2
|
||||
* and O_TMPFILE on Linux, with additional hardening; BSD
|
||||
* projects only have openat so the code uses that there.
|
||||
*
|
||||
* Many programs rely on mktemp, and they use TMPDIR in a way
|
||||
* that is quite insecure. Mkhtemp intends to change that,
|
||||
* quite dramatically, with: userspace sandbox (and use OS
|
||||
* level options e.g. OBSD pledge where available), constant
|
||||
* identity/ownership checks on files, MUCH stricter ownership
|
||||
* restrictions (e.g. enforce sticky bit policy on world-
|
||||
* writeable tmpdirs), preventing operation on other people's
|
||||
* files (only your own files) - even root is restricted,
|
||||
* depending on how the code is compiled. Please read the code.
|
||||
*
|
||||
* This is the utility version, which makes use of the also-
|
||||
* included library. No docs yet - source code are the docs,
|
||||
* and the (ever evolving, and hardening) specification.
|
||||
*
|
||||
* This was written from scratch, for use in nvmutil, and
|
||||
* it is designed to be portable (BSD, Linux). Patches
|
||||
* very much welcome.
|
||||
*
|
||||
* WARNING: This is MUCH stricter than every other mktemp
|
||||
* implementation, even more so than mkdtemp or
|
||||
* the OpenBSD version of mkstemp. It *will* break,
|
||||
* or more specifically, reveal the flaws in, almost
|
||||
* every major critical infrastructure, because most
|
||||
* people already use mktemp extremely insecurely.
|
||||
*
|
||||
* This tool is written by me, for me, and also Libreboot, but
|
||||
* it will be summitted for review to various Linux distros
|
||||
* and BSD projects once it has reached maturity.
|
||||
*/
|
||||
|
||||
#if defined(__linux__) && !defined(_GNU_SOURCE)
|
||||
/* for openat2 on linux */
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/param.h> /* pledge(2) */
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "include/common.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
#if defined (PATH_LEN) && \
|
||||
(PATH_LEN) >= 256
|
||||
size_t maxlen = PATH_LEN;
|
||||
#else
|
||||
size_t maxlen = 4096;
|
||||
#endif
|
||||
size_t len;
|
||||
size_t tlen;
|
||||
size_t xc = 0;
|
||||
|
||||
char *tmpdir = NULL;
|
||||
char *template = NULL;
|
||||
char *p;
|
||||
char *s = NULL;
|
||||
char *rp;
|
||||
char resolved[maxlen];
|
||||
char c;
|
||||
|
||||
int fd = -1;
|
||||
int type = MKHTEMP_FILE;
|
||||
int stfu = 0; /* -q option */
|
||||
|
||||
if (lbgetprogname(argv[0]) == NULL)
|
||||
err_no_cleanup(stfu, errno, "could not set progname");
|
||||
|
||||
/* https://man.openbsd.org/pledge.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
goto err_usage;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
while ((c =
|
||||
getopt(argc, argv, "qdp:")) != -1) {
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
type = MKHTEMP_DIR;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
tmpdir = optarg;
|
||||
break;
|
||||
|
||||
case 'q': /* don't print errors */
|
||||
/* (exit status unchanged) */
|
||||
stfu = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto err_usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
template = argv[optind];
|
||||
if (optind + 1 < argc)
|
||||
goto err_usage;
|
||||
|
||||
/* custom template e.g. foo.XXXXXXXXXXXXXXXXXXXXX */
|
||||
if (template != NULL) {
|
||||
if (slen(template, maxlen, &tlen) < 0)
|
||||
err_no_cleanup(stfu, EINVAL,
|
||||
"invalid template");
|
||||
|
||||
for (p = template + tlen;
|
||||
p > template && *--p == 'X'; xc++);
|
||||
|
||||
if (xc < 3) /* the gnu mktemp errs on less than 3 */
|
||||
err_no_cleanup(stfu, EINVAL,
|
||||
"template must have 3 X or more on end (12+ advised");
|
||||
}
|
||||
|
||||
/* user supplied -p PATH - WARNING:
|
||||
* this permits symlinks, but only here,
|
||||
* not in the library, so they are resolved
|
||||
* here first, and *only here*. the mkhtemp
|
||||
* library blocks them. be careful
|
||||
* when using -p
|
||||
*/
|
||||
if (tmpdir != NULL) {
|
||||
rp = realpath(tmpdir, resolved);
|
||||
if (rp == NULL)
|
||||
err_no_cleanup(stfu, errno, "%s", tmpdir);
|
||||
|
||||
tmpdir = resolved;
|
||||
}
|
||||
|
||||
if (new_tmp_common(&fd, &s, type,
|
||||
tmpdir, template) < 0)
|
||||
err_no_cleanup(stfu, errno, "%s", s);
|
||||
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 509
|
||||
if (pledge("stdio", NULL) == -1)
|
||||
err_no_cleanup(stfu, errno, "pledge, exit");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (s == NULL)
|
||||
err_no_cleanup(stfu, EFAULT, "bad string initialisation");
|
||||
if (*s == '\0')
|
||||
err_no_cleanup(stfu, EFAULT, "empty string initialisation");
|
||||
if (slen(s, maxlen, &len) < 0)
|
||||
err_no_cleanup(stfu, EFAULT, "unterminated string initialisiert");
|
||||
|
||||
printf("%s\n", s);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
err_usage:
|
||||
err_no_cleanup(stfu, EINVAL,
|
||||
"usage: %s [-d] [-p dir] [template]\n", getnvmprogname());
|
||||
}/*
|
||||
|
||||
|
||||
( >:3 )
|
||||
/| |\
|
||||
/ \
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
132
util/libreboot-utils/nvmutil.c
Normal file
132
util/libreboot-utils/nvmutil.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* This tool lets you modify Intel GbE NVM (Gigabit Ethernet
|
||||
* Non-Volatile Memory) images, e.g. change the MAC address.
|
||||
* These images configure your Intel Gigabit Ethernet adapter.
|
||||
*/
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
/* for pledge/unveil test:
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/common.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct xstate *x;
|
||||
|
||||
struct commands *cmd;
|
||||
struct xfile *f;
|
||||
|
||||
size_t c;
|
||||
|
||||
if (lbgetprogname(argv[0]) == NULL)
|
||||
err_no_cleanup(0, errno, "could not set progname");
|
||||
|
||||
/* https://man.openbsd.org/pledge.2
|
||||
https://man.openbsd.org/unveil.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 604
|
||||
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
|
||||
err_no_cleanup(0, errno, "pledge plus unveil, main");
|
||||
if (unveil("/dev/null", "r") == -1)
|
||||
err_no_cleanup(0, errno, "unveil r: /dev/null");
|
||||
#elif (OpenBSD) >= 509
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
err_no_cleanup(0, errno, "pledge, main");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef S_ISREG
|
||||
err_no_cleanup(0, ECANCELED,
|
||||
"Can't determine file types (S_ISREG undefined)");
|
||||
#endif
|
||||
#if ((CHAR_BIT) != 8)
|
||||
err_no_cleanup(0, ECANCELED, "Unsupported char size");
|
||||
#endif
|
||||
|
||||
x = xstart(argc, argv);
|
||||
|
||||
if (x == NULL)
|
||||
err_no_cleanup(0, ECANCELED, "NULL state on init");
|
||||
|
||||
/* parse user command */
|
||||
/* TODO: CHECK ACCESSES VIA xstatus() */
|
||||
set_cmd(argc, argv);
|
||||
set_cmd_args(argc, argv);
|
||||
|
||||
cmd = &x->cmd[x->i];
|
||||
f = &x->f;
|
||||
|
||||
/* https://man.openbsd.org/pledge.2
|
||||
https://man.openbsd.org/unveil.2 */
|
||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||
#if (OpenBSD) >= 604
|
||||
|
||||
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
|
||||
if (unveil(us.f.fname, "r") == -1)
|
||||
b0rk(errno, "%s: unveil r", us.f.fname);
|
||||
} else {
|
||||
if (unveil(us.f.fname, "rwc") == -1)
|
||||
b0rk(errno, "%s: unveil rw", us.f.fname);
|
||||
}
|
||||
|
||||
if (unveil(us.f.tname, "rwc") == -1)
|
||||
b0rk(errno, "unveil rwc: %s", us.f.tname);
|
||||
|
||||
if (unveil(NULL, NULL) == -1)
|
||||
b0rk(errno, "unveil block (rw)");
|
||||
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
b0rk(errno, "pledge (kill unveil)");
|
||||
|
||||
#elif (OpenBSD) >= 509
|
||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||
b0rk(errno, "pledge");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (cmd->run == NULL)
|
||||
b0rk(errno, "Command not set");
|
||||
|
||||
|
||||
sanitize_command_list();
|
||||
|
||||
open_gbe_file();
|
||||
|
||||
copy_gbe();
|
||||
read_checksums();
|
||||
|
||||
cmd->run();
|
||||
|
||||
for (c = 0; c < items(x->cmd); c++)
|
||||
x->cmd[c].run = cmd_helper_err;
|
||||
|
||||
if ((cmd->flags & O_ACCMODE) == O_RDWR)
|
||||
write_to_gbe_bin();
|
||||
|
||||
if (exit_cleanup() == -1)
|
||||
b0rk(EIO, "%s: close", f->fname);
|
||||
|
||||
if (f->io_err_gbe_bin)
|
||||
b0rk(EIO, "%s: error writing final file");
|
||||
|
||||
free_if_null(&f->tname);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,890 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
/* check that a file changed
|
||||
*/
|
||||
|
||||
int
|
||||
same_file(int fd, struct stat *st_old,
|
||||
int check_size)
|
||||
{
|
||||
struct stat st;
|
||||
int saved_errno = errno;
|
||||
|
||||
if (st_old == NULL || fd < 0)
|
||||
goto err_same_file;
|
||||
|
||||
if (fstat(fd, &st) == -1)
|
||||
return -1;
|
||||
|
||||
if (st.st_dev != st_old->st_dev ||
|
||||
st.st_ino != st_old->st_ino ||
|
||||
!S_ISREG(st.st_mode))
|
||||
goto err_same_file;
|
||||
|
||||
if (check_size &&
|
||||
st.st_size != st_old->st_size)
|
||||
goto err_same_file;
|
||||
|
||||
errno = saved_errno;
|
||||
return 0;
|
||||
|
||||
err_same_file:
|
||||
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* open() but with abort traps
|
||||
*/
|
||||
|
||||
void
|
||||
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
|
||||
{
|
||||
if ((*fd_ptr = open(path, flags)) == -1)
|
||||
err(errno, "%s", path);
|
||||
|
||||
if (fstat(*fd_ptr, st) == -1)
|
||||
err(errno, "%s: stat", path);
|
||||
|
||||
if (!S_ISREG(st->st_mode))
|
||||
err(errno, "%s: not a regular file", path);
|
||||
|
||||
if (lseek(*fd_ptr, 0, SEEK_CUR) == (off_t)-1)
|
||||
err(errno, "%s: file not seekable", path);
|
||||
}
|
||||
|
||||
/* fsync() the directory of a file,
|
||||
* useful for atomic writes
|
||||
*/
|
||||
|
||||
int
|
||||
fsync_dir(const char *path)
|
||||
{
|
||||
int saved_errno = errno;
|
||||
|
||||
unsigned long pathlen;
|
||||
unsigned long maxlen;
|
||||
|
||||
char *dirbuf;
|
||||
int dirfd;
|
||||
|
||||
char *slash;
|
||||
|
||||
struct stat st;
|
||||
|
||||
#if defined(PATH_LEN) && \
|
||||
(PATH_LEN) >= 256
|
||||
maxlen = PATH_LEN;
|
||||
#else
|
||||
maxlen = 1024;
|
||||
#endif
|
||||
|
||||
dirbuf = NULL;
|
||||
dirfd = -1;
|
||||
|
||||
pathlen = xstrxlen(path, maxlen);
|
||||
|
||||
if (pathlen >= maxlen) {
|
||||
fprintf(stderr, "Path too long for fsync_parent_dir\n");
|
||||
goto err_fsync_dir;
|
||||
}
|
||||
|
||||
if (pathlen == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
goto err_fsync_dir;
|
||||
}
|
||||
|
||||
dirbuf = malloc(pathlen + 1);
|
||||
if (dirbuf == NULL)
|
||||
goto err_fsync_dir;
|
||||
|
||||
memcpy(dirbuf, path, pathlen + 1);
|
||||
slash = strrchr(dirbuf, '/');
|
||||
|
||||
if (slash != NULL) {
|
||||
*slash = '\0';
|
||||
if (*dirbuf == '\0') {
|
||||
dirbuf[0] = '/';
|
||||
dirbuf[1] = '\0';
|
||||
}
|
||||
} else {
|
||||
dirbuf[0] = '.';
|
||||
dirbuf[1] = '\0';
|
||||
}
|
||||
|
||||
dirfd = open(dirbuf, O_RDONLY
|
||||
#ifdef O_DIRECTORY
|
||||
| O_DIRECTORY
|
||||
#endif
|
||||
#ifdef O_NOFOLLOW
|
||||
| O_NOFOLLOW
|
||||
#endif
|
||||
);
|
||||
if (dirfd == -1)
|
||||
goto err_fsync_dir;
|
||||
|
||||
if (fstat(dirfd, &st) < 0)
|
||||
goto err_fsync_dir;
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
fprintf(stderr, "%s: not a directory\n", dirbuf);
|
||||
goto err_fsync_dir;
|
||||
}
|
||||
|
||||
/* sync file on disk */
|
||||
if (fsync_on_eintr(dirfd) == -1)
|
||||
goto err_fsync_dir;
|
||||
|
||||
if (close_on_eintr(dirfd) == -1)
|
||||
goto err_fsync_dir;
|
||||
|
||||
if (dirbuf != NULL)
|
||||
free(dirbuf);
|
||||
|
||||
errno = saved_errno;
|
||||
return 0;
|
||||
|
||||
err_fsync_dir:
|
||||
if (!errno)
|
||||
errno = EIO;
|
||||
|
||||
if (errno != saved_errno)
|
||||
fprintf(stderr, "%s: %s\n", path, strerror(errno));
|
||||
|
||||
if (dirbuf != NULL)
|
||||
free(dirbuf);
|
||||
|
||||
if (dirfd > -1)
|
||||
close_on_eintr(dirfd);
|
||||
|
||||
errno = saved_errno;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* returns ptr to path (string). if local>0:
|
||||
* make tmpfile in the same directory as the
|
||||
* file. if local==0, use TMPDIR
|
||||
*
|
||||
* if local==0, the 3rd argument is ignored
|
||||
*/
|
||||
|
||||
char *
|
||||
new_tmpfile(int *fd, int local, const char *path)
|
||||
{
|
||||
unsigned long maxlen;
|
||||
struct stat st;
|
||||
|
||||
/* please do not modify the
|
||||
* strings or I will get mad
|
||||
*/
|
||||
char tmp_none[] = "";
|
||||
char tmp_default[] = "/tmp";
|
||||
char default_tmpname[] = "tmpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
|
||||
char *tmpname;
|
||||
|
||||
char *base = NULL;
|
||||
char *dest = NULL;
|
||||
|
||||
unsigned long tmpdir_len = 0;
|
||||
unsigned long tmpname_len = 0;
|
||||
unsigned long tmppath_len = 0;
|
||||
|
||||
int fd_tmp = -1;
|
||||
int flags;
|
||||
|
||||
#if defined(PATH_LEN) && \
|
||||
(PATH_LEN) >= 256
|
||||
maxlen = PATH_LEN;
|
||||
#else
|
||||
maxlen = 1024;
|
||||
#endif
|
||||
|
||||
tmpname = default_tmpname;
|
||||
if (local) {
|
||||
if (path == NULL)
|
||||
goto err_new_tmpfile;
|
||||
if (*path == '\0')
|
||||
goto err_new_tmpfile;
|
||||
|
||||
if (stat(path, &st) == -1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
goto err_new_tmpfile;
|
||||
|
||||
tmpname = (char *)path;
|
||||
}
|
||||
|
||||
if (local) {
|
||||
base = tmp_none;
|
||||
|
||||
/* appended to filename for tmp:
|
||||
*/
|
||||
tmpdir_len = xstrxlen(default_tmpname, maxlen);
|
||||
} else {
|
||||
base = get_tmpdir();
|
||||
|
||||
if (base == NULL)
|
||||
base = tmp_default;
|
||||
if (*base == '\0')
|
||||
base = tmp_default;
|
||||
|
||||
tmpdir_len = xstrxlen(base, maxlen);
|
||||
}
|
||||
|
||||
tmpname_len = xstrxlen(tmpname, maxlen);
|
||||
|
||||
tmppath_len = tmpdir_len + tmpname_len;
|
||||
++tmppath_len; /* for '/' or '.' */
|
||||
|
||||
/* max length -1 of maxlen
|
||||
* for termination
|
||||
*/
|
||||
if (tmpdir_len > maxlen - tmpname_len - 1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
/* +1 for NULL */
|
||||
dest = malloc(tmppath_len + 1);
|
||||
if (dest == NULL)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
if (local) {
|
||||
|
||||
*dest = '.'; /* hidden file */
|
||||
|
||||
memcpy(dest + (unsigned long)1, tmpname, tmpname_len);
|
||||
|
||||
memcpy(dest + (unsigned long)1 + tmpname_len,
|
||||
default_tmpname, tmpdir_len);
|
||||
} else {
|
||||
|
||||
memcpy(dest, base, tmpdir_len);
|
||||
|
||||
dest[tmpdir_len] = '/';
|
||||
|
||||
memcpy(dest + tmpdir_len + 1, tmpname, tmpname_len);
|
||||
}
|
||||
|
||||
dest[tmppath_len] = '\0';
|
||||
|
||||
fd_tmp = mkstemp_n(dest);
|
||||
if (fd_tmp == -1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
if (fchmod(fd_tmp, 0600) == -1)
|
||||
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;
|
||||
|
||||
if (lock_file(fd_tmp, flags) == -1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
if (fstat(fd_tmp, &st) == -1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
/*
|
||||
* Extremely defensive
|
||||
* likely pointless checks
|
||||
*/
|
||||
|
||||
/* check if it's a file */
|
||||
if (!S_ISREG(st.st_mode))
|
||||
goto err_new_tmpfile;
|
||||
|
||||
/* check if it's seekable */
|
||||
if (lseek(fd_tmp, 0, SEEK_CUR) == (off_t)-1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
/* tmpfile has >1 hardlinks */
|
||||
if (st.st_nlink > 1)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
/* tmpfile unlinked while opened */
|
||||
if (st.st_nlink == 0)
|
||||
goto err_new_tmpfile;
|
||||
|
||||
*fd = fd_tmp;
|
||||
|
||||
return dest;
|
||||
|
||||
err_new_tmpfile:
|
||||
|
||||
if (dest != NULL)
|
||||
free(dest);
|
||||
|
||||
if (fd_tmp > -1)
|
||||
close_on_eintr(fd_tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
lock_file(int fd, int flags)
|
||||
{
|
||||
struct flock fl;
|
||||
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
|
||||
if ((flags & O_ACCMODE) == O_RDONLY)
|
||||
fl.l_type = F_RDLCK;
|
||||
else
|
||||
fl.l_type = F_WRLCK;
|
||||
|
||||
fl.l_whence = SEEK_SET;
|
||||
|
||||
if (fcntl(fd, F_SETLK, &fl) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return TMPDIR, or fall back
|
||||
* to portable defaults
|
||||
*/
|
||||
|
||||
char *
|
||||
get_tmpdir(void)
|
||||
{
|
||||
char *t;
|
||||
struct stat st;
|
||||
|
||||
t = getenv("TMPDIR");
|
||||
|
||||
if (t && *t) {
|
||||
|
||||
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))
|
||||
return "/tmp";
|
||||
|
||||
if (stat("/var/tmp", &st) == 0 && S_ISDIR(st.st_mode))
|
||||
return "/var/tmp";
|
||||
|
||||
return ".";
|
||||
}
|
||||
|
||||
/* portable mkstemp
|
||||
*/
|
||||
|
||||
int
|
||||
mkstemp_n(char *template)
|
||||
{
|
||||
int fd;
|
||||
unsigned long i, j;
|
||||
unsigned long len;
|
||||
char *p;
|
||||
|
||||
unsigned long xc = 0;
|
||||
|
||||
static char ch[] =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
unsigned long r;
|
||||
#if defined(PATH_LEN) && \
|
||||
(PATH_LEN) >= 256
|
||||
unsigned long max_len = PATH_LEN;
|
||||
#else
|
||||
unsigned long max_len = 4096;
|
||||
#endif
|
||||
|
||||
len = xstrxlen(template, max_len);
|
||||
|
||||
if (len < 6) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = template + len;
|
||||
|
||||
while (p > template && p[-1] == 'X') {
|
||||
--p;
|
||||
++xc;
|
||||
}
|
||||
|
||||
if (xc < 6) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 200; i++) {
|
||||
|
||||
for (j = 0; j < xc; j++) {
|
||||
|
||||
r = rlong();
|
||||
|
||||
p[j] = ch[(unsigned long)(r >> 1) % (sizeof(ch) - 1)];
|
||||
}
|
||||
|
||||
fd = open(template,
|
||||
O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC, 0600);
|
||||
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
if (errno != EEXIST)
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Safe I/O functions wrapping around
|
||||
* read(), write() and providing a portable
|
||||
* analog of both pread() and pwrite().
|
||||
* These functions are designed for maximum
|
||||
* robustness, checking NULL inputs, overflowed
|
||||
* outputs, and all kinds of errors that the
|
||||
* standard libc functions don't.
|
||||
*
|
||||
* Looping on EINTR and EAGAIN is supported.
|
||||
* EINTR/EAGAIN looping is done indefinitely.
|
||||
*/
|
||||
|
||||
/* rw_file_exact() - Read perfectly or die
|
||||
*
|
||||
* Read/write, and absolutely insist on an
|
||||
* absolute read; e.g. if 100 bytes are
|
||||
* requested, this MUST return 100.
|
||||
*
|
||||
* This function will never return zero.
|
||||
* It will only return below (error),
|
||||
* or above (success). On error, -1 is
|
||||
* returned and errno is set accordingly.
|
||||
*
|
||||
* Zero-byte returns are not allowed.
|
||||
* It will re-spin a finite number of
|
||||
* times upon zero-return, to recover,
|
||||
* otherwise it will return an error.
|
||||
*/
|
||||
|
||||
long
|
||||
rw_file_exact(int fd, unsigned char *mem, unsigned long nrw,
|
||||
off_t off, int rw_type, int loop_eagain,
|
||||
int loop_eintr, unsigned long max_retries,
|
||||
int off_reset)
|
||||
{
|
||||
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 (rval >= 0 && (unsigned long)rval > (nrw - rc))
|
||||
goto err_rw_file_exact;
|
||||
|
||||
rc += rval;
|
||||
if ((unsigned long)rc >= nrw)
|
||||
break;
|
||||
|
||||
mem_cur = (void *)(mem + (unsigned long)rc);
|
||||
nrw_cur = (unsigned long)(nrw - (unsigned long)rc);
|
||||
if (off < 0)
|
||||
goto err_rw_file_exact;
|
||||
off_cur = off + (off_t)rc;
|
||||
|
||||
rval = prw(fd, mem_cur, nrw_cur, off_cur,
|
||||
rw_type, loop_eagain, loop_eintr,
|
||||
off_reset);
|
||||
|
||||
if (rval < 0)
|
||||
return -1;
|
||||
|
||||
if (rval == 0) {
|
||||
if (retries_on_zero++ < max_retries)
|
||||
continue;
|
||||
goto err_rw_file_exact;
|
||||
}
|
||||
|
||||
retries_on_zero = 0;
|
||||
}
|
||||
|
||||
if ((unsigned long)rc != nrw)
|
||||
goto err_rw_file_exact;
|
||||
|
||||
return rw_over_nrw(rc, nrw);
|
||||
|
||||
err_rw_file_exact:
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* prw() - portable read-write with more
|
||||
* safety checks than barebones libc
|
||||
*
|
||||
* portable pwrite/pread on request, or real
|
||||
* pwrite/pread libc functions can be used.
|
||||
* the portable (non-libc) pread/pwrite is not
|
||||
* thread-safe, because it does not prevent or
|
||||
* mitigate race conditions on file descriptors
|
||||
*
|
||||
* If you need real pwrite/pread, just compile
|
||||
* with flag: HAVE_REAL_PREAD_PWRITE=1
|
||||
*
|
||||
* A fallback is provided for regular read/write.
|
||||
* rw_type can be IO_READ (read), IO_WRITE (write),
|
||||
* IO_PREAD (pread) or IO_PWRITE
|
||||
*
|
||||
* loop_eagain does a retry loop on EAGAIN if set
|
||||
* loop_eintr does a retry loop on EINTR if set
|
||||
*
|
||||
* race conditions on non-libc pread/pwrite:
|
||||
* if a file offset changes, abort, to mitage.
|
||||
*
|
||||
* off_reset 1: reset the file offset *once* if
|
||||
* a change was detected, assuming
|
||||
* nothing else is touching it now
|
||||
* off_reset 0: never reset if changed
|
||||
*/
|
||||
|
||||
long
|
||||
prw(int fd, void *mem, unsigned long nrw,
|
||||
off_t off, int rw_type,
|
||||
int loop_eagain, int loop_eintr,
|
||||
int off_reset)
|
||||
{
|
||||
long r;
|
||||
int positional_rw;
|
||||
struct stat st;
|
||||
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
|
||||
HAVE_REAL_PREAD_PWRITE < 1
|
||||
int saved_errno;
|
||||
off_t verified;
|
||||
off_t off_orig;
|
||||
off_t off_last;
|
||||
#endif
|
||||
|
||||
if (io_args(fd, mem, nrw, off, rw_type)
|
||||
== -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = -1;
|
||||
|
||||
/* do not use loop_eagain on
|
||||
* normal files
|
||||
*/
|
||||
|
||||
if (!loop_eagain) {
|
||||
/* check whether the file
|
||||
* changed
|
||||
*/
|
||||
|
||||
if (check_file(fd, &st) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rw_type >= IO_PREAD)
|
||||
positional_rw = 1; /* pread/pwrite */
|
||||
else
|
||||
positional_rw = 0; /* read/write */
|
||||
|
||||
try_rw_again:
|
||||
|
||||
if (!positional_rw) {
|
||||
#if defined(HAVE_REAL_PREAD_PWRITE) && \
|
||||
HAVE_REAL_PREAD_PWRITE > 0
|
||||
real_pread_pwrite:
|
||||
#endif
|
||||
if (rw_type == IO_WRITE)
|
||||
r = write(fd, mem, nrw);
|
||||
else if (rw_type == IO_READ)
|
||||
r = read(fd, mem, nrw);
|
||||
#if defined(HAVE_REAL_PREAD_PWRITE) && \
|
||||
HAVE_REAL_PREAD_PWRITE > 0
|
||||
else if (rw_type == IO_PWRITE)
|
||||
r = pwrite(fd, mem, nrw, off);
|
||||
else if (rw_type == IO_PREAD)
|
||||
r = pread(fd, mem, nrw, off);
|
||||
#endif
|
||||
|
||||
if (r == -1 && (errno == try_err(loop_eintr, EINTR)
|
||||
|| errno == try_err(loop_eagain, EAGAIN)))
|
||||
goto try_rw_again;
|
||||
|
||||
return rw_over_nrw(r, nrw);
|
||||
}
|
||||
|
||||
#if defined(HAVE_REAL_PREAD_PWRITE) && \
|
||||
HAVE_REAL_PREAD_PWRITE > 0
|
||||
goto real_pread_pwrite;
|
||||
#else
|
||||
if ((off_orig = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
|
||||
loop_eagain, loop_eintr)) == (off_t)-1) {
|
||||
r = -1;
|
||||
} else if (lseek_on_eintr(fd, off, SEEK_SET,
|
||||
loop_eagain, loop_eintr) == (off_t)-1) {
|
||||
r = -1;
|
||||
} else {
|
||||
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
|
||||
loop_eagain, loop_eintr);
|
||||
|
||||
/* abort if the offset changed,
|
||||
* indicating race condition. if
|
||||
* off_reset enabled, reset *ONCE*
|
||||
*/
|
||||
|
||||
if (off_reset && off != verified)
|
||||
lseek_on_eintr(fd, off, SEEK_SET,
|
||||
loop_eagain, loop_eintr);
|
||||
|
||||
do {
|
||||
/* check offset again, repeatedly.
|
||||
* even if off_reset is set, this
|
||||
* aborts if offsets change again
|
||||
*/
|
||||
|
||||
verified = lseek_on_eintr(fd, (off_t)0, SEEK_CUR,
|
||||
loop_eagain, loop_eintr);
|
||||
|
||||
if (off != verified)
|
||||
goto err_prw;
|
||||
|
||||
if (rw_type == IO_PREAD)
|
||||
r = read(fd, mem, nrw);
|
||||
else if (rw_type == IO_PWRITE)
|
||||
r = write(fd, mem, nrw);
|
||||
|
||||
if (rw_over_nrw(r, nrw) == -1) {
|
||||
errno = EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (r == -1 &&
|
||||
(errno == try_err(loop_eintr, EINTR) ||
|
||||
errno == try_err(loop_eagain, EAGAIN)));
|
||||
}
|
||||
|
||||
saved_errno = errno;
|
||||
|
||||
off_last = lseek_on_eintr(fd, off_orig, SEEK_SET,
|
||||
loop_eagain, loop_eintr);
|
||||
|
||||
if (off_last != off_orig) {
|
||||
errno = saved_errno;
|
||||
goto err_prw;
|
||||
}
|
||||
|
||||
errno = saved_errno;
|
||||
|
||||
return rw_over_nrw(r, nrw);
|
||||
#endif
|
||||
|
||||
err_prw:
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
io_args(int fd, void *mem, unsigned long nrw,
|
||||
off_t off, int rw_type)
|
||||
{
|
||||
/* obviously */
|
||||
if (mem == NULL)
|
||||
goto err_io_args;
|
||||
|
||||
/* uninitialised fd */
|
||||
if (fd < 0)
|
||||
goto err_io_args;
|
||||
|
||||
/* negative offset */
|
||||
if (off < 0)
|
||||
goto err_io_args;
|
||||
|
||||
/* prevent zero-byte rw */
|
||||
if (!nrw)
|
||||
goto err_io_args;
|
||||
|
||||
/* prevent overflow */
|
||||
if (nrw > (unsigned long)X_LONG_MAX)
|
||||
goto err_io_args;
|
||||
|
||||
/* prevent overflow */
|
||||
if (((unsigned long)off + nrw) < (unsigned long)off)
|
||||
goto err_io_args;
|
||||
|
||||
if (rw_type > IO_PWRITE)
|
||||
goto err_io_args;
|
||||
|
||||
return 0;
|
||||
|
||||
err_io_args:
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
check_file(int fd, struct stat *st)
|
||||
{
|
||||
if (fstat(fd, st) == -1)
|
||||
goto err_is_file;
|
||||
|
||||
if (!S_ISREG(st->st_mode))
|
||||
goto err_is_file;
|
||||
|
||||
return 0;
|
||||
|
||||
err_is_file:
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* POSIX can say whatever it wants.
|
||||
* specification != implementation
|
||||
*/
|
||||
|
||||
long
|
||||
rw_over_nrw(long r, unsigned long nrw)
|
||||
{
|
||||
/* not a libc bug, but we
|
||||
* don't like the number zero
|
||||
*/
|
||||
if (!nrw)
|
||||
goto err_rw_over_nrw;
|
||||
|
||||
if (r == -1)
|
||||
return r;
|
||||
|
||||
if ((unsigned long)
|
||||
r > X_LONG_MAX) {
|
||||
|
||||
/* Theoretical buggy libc
|
||||
* check. Extremely academic.
|
||||
*
|
||||
* Specifications never
|
||||
* allow this return value
|
||||
* to exceed SSIZE_T, but
|
||||
* spec != implementation
|
||||
*
|
||||
* Check this after using
|
||||
* [p]read() or [p]write()
|
||||
*
|
||||
* NOTE: here, we assume
|
||||
* long integers are the
|
||||
* same size as SSIZE_T
|
||||
*/
|
||||
|
||||
goto err_rw_over_nrw;
|
||||
}
|
||||
|
||||
/* Theoretical buggy libc:
|
||||
* Should never return a number of
|
||||
* bytes above the requested length.
|
||||
*/
|
||||
if ((unsigned long)r > nrw)
|
||||
goto err_rw_over_nrw;
|
||||
|
||||
return r;
|
||||
|
||||
err_rw_over_nrw:
|
||||
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !defined(HAVE_REAL_PREAD_PWRITE) || \
|
||||
HAVE_REAL_PREAD_PWRITE < 1
|
||||
off_t
|
||||
lseek_on_eintr(int fd, off_t off, int whence,
|
||||
int loop_eagain, int loop_eintr)
|
||||
{
|
||||
off_t old;
|
||||
|
||||
old = -1;
|
||||
|
||||
do {
|
||||
old = lseek(fd, off, whence);
|
||||
} while (old == (off_t)-1 && (
|
||||
errno == try_err(loop_eintr, EINTR) ||
|
||||
errno == try_err(loop_eagain, EAGAIN)));
|
||||
|
||||
return old;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
try_err(int loop_err, int errval)
|
||||
{
|
||||
if (loop_err)
|
||||
return errval;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
close_on_eintr(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;
|
||||
}
|
||||
|
||||
int
|
||||
fsync_on_eintr(int fd)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
r = fsync(fd);
|
||||
} while (r == -1 && errno == EINTR);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1,349 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* Numerical functions.
|
||||
*/
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#if defined(FALLBACK_RAND_1989) && \
|
||||
(FALLBACK_RAND_1989) > 0
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#if !((defined(__OpenBSD__) && (OpenBSD) >= 201) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__APPLE__))
|
||||
#include <fcntl.h> /* if not arc4random: /dev/urandom */
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#if defined(FALLBACK_RAND_1989) && \
|
||||
(FALLBACK_RAND_1989) > 0
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
unsigned short
|
||||
hextonum(char ch_s)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
ch = (unsigned char)ch_s;
|
||||
|
||||
if ((unsigned int)(ch - '0') <= 9)
|
||||
return ch - '0';
|
||||
|
||||
ch |= 0x20;
|
||||
|
||||
if ((unsigned int)(ch - 'a') <= 5)
|
||||
return ch - 'a' + 10;
|
||||
|
||||
if (ch == '?' || ch == 'x')
|
||||
return (unsigned short)rlong() & 0xf;
|
||||
|
||||
return 16; /* invalid character */
|
||||
}
|
||||
|
||||
/* Random numbers
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
rlong(void)
|
||||
{
|
||||
#if !(defined(FALLBACK_RAND_1989) && \
|
||||
((FALLBACK_RAND_1989) > 0))
|
||||
#if (defined(__OpenBSD__) && (OpenBSD) >= 201) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || defined(__APPLE__)
|
||||
|
||||
unsigned long rval;
|
||||
arc4random_buf(&rval, sizeof(unsigned long));
|
||||
|
||||
return rval;
|
||||
#else
|
||||
static int fd = -1;
|
||||
static long nr = -1;
|
||||
static unsigned long off = 0;
|
||||
#if defined (BUFSIZ)
|
||||
static char rbuf[BUFSIZ];
|
||||
#else
|
||||
#ifndef PORTABLE
|
||||
static char rbuf[4096];
|
||||
#elif ((PORTABLE) > 0)
|
||||
static char rbuf[256]; /* scarce memory on old systems */
|
||||
#else
|
||||
static char rbuf[4096]; /* typical 32-bit BUFSIZ */
|
||||
#endif
|
||||
#endif
|
||||
unsigned long rval;
|
||||
long new_nr;
|
||||
|
||||
int retries = 0;
|
||||
int max_retries = 100;
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(HAVE_GETRANDOM) || \
|
||||
defined(HAVE_GETRANDOM_SYSCALL)
|
||||
|
||||
/* linux getrandom()
|
||||
*
|
||||
* we *can* use arc4random on
|
||||
* modern linux, but not on
|
||||
* every libc. better use the
|
||||
* official linux function
|
||||
*
|
||||
* similar benefits to arc4random
|
||||
* e.g. works in chroot, blocks
|
||||
* until it has enough entropy,
|
||||
* and works even when /dev/urandom
|
||||
* is available (doesn't use it);
|
||||
* it's generally more reliable
|
||||
*/
|
||||
|
||||
if (fallback_rand_getrandom(&rval, sizeof(rval)) == 0)
|
||||
return rval;
|
||||
|
||||
/*
|
||||
* now fall back to urandom if getrandom failed:
|
||||
*/
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* reading from urandom is inherently
|
||||
* unreliable on old systems, even if
|
||||
* newer systems make it more reliable
|
||||
*
|
||||
* modern linux/bsd make it safe, but
|
||||
* we have to assume that someone is
|
||||
* compiling this on linux from 1999
|
||||
*
|
||||
* this logic therefore applies various
|
||||
* tricks to mitigate possible os bugs
|
||||
*/
|
||||
|
||||
retry_urandom_read:
|
||||
|
||||
if (++retries > max_retries)
|
||||
goto rlong_next;
|
||||
|
||||
if (nr < 0 || nr < (long)sizeof(unsigned long)) {
|
||||
|
||||
if (fd < 0) {
|
||||
|
||||
fd = open("/dev/urandom",
|
||||
O_RDONLY | O_BINARY | O_NOFOLLOW |
|
||||
O_CLOEXEC);
|
||||
|
||||
#ifdef USE_OLD_DEV_RANDOM
|
||||
#if (USE_OLD_DEV_RANDOM) > 0
|
||||
/* WARNING:
|
||||
* /dev/random may block
|
||||
* forever and does **NOT**
|
||||
* guarantee better entropy
|
||||
* on old systems
|
||||
*
|
||||
* only use it if needed
|
||||
*/
|
||||
|
||||
if (fd < 0)
|
||||
fd = open("/dev/random",
|
||||
O_RDONLY | O_BINARY | O_NOFOLLOW |
|
||||
O_CLOEXEC);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (fd < 0)
|
||||
goto retry_urandom_read;
|
||||
|
||||
retries = 0;
|
||||
}
|
||||
|
||||
new_nr = rw_file_exact(fd, (unsigned char *)rbuf,
|
||||
sizeof(rbuf), 0, IO_READ, LOOP_EAGAIN,
|
||||
LOOP_EINTR, MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||
|
||||
if (new_nr < 0 || new_nr < (long)sizeof(rbuf))
|
||||
goto retry_urandom_read;
|
||||
|
||||
/* only reset buffer after successful refill */
|
||||
nr = new_nr;
|
||||
off = 0;
|
||||
|
||||
/* to mitigate file descriptor
|
||||
* injection, we do not re-use
|
||||
* the same descriptor each time
|
||||
*/
|
||||
(void) close_on_eintr(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
fd = -1;
|
||||
retries = 0;
|
||||
|
||||
memcpy(&rval, rbuf + off, sizeof(unsigned long));
|
||||
|
||||
nr -= (long)sizeof(unsigned long);
|
||||
off += sizeof(unsigned long);
|
||||
|
||||
return rval;
|
||||
|
||||
rlong_next:
|
||||
|
||||
fd = -1;
|
||||
off = 0;
|
||||
nr = -1;
|
||||
|
||||
err(EIO, "Can't read from /dev/[ua]random");
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
#else /* FALLBACK_RAND_1989 */
|
||||
/* your computer is from a museum
|
||||
*/
|
||||
unsigned long mix = 0;
|
||||
int nr;
|
||||
|
||||
/* 100 times, for entropy
|
||||
*/
|
||||
for (nr = 0; nr < 100; nr++)
|
||||
mix ^= fallback_rand_1989();
|
||||
|
||||
/* 101 times ;)
|
||||
*/
|
||||
return fallback_rand_1989();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !(defined(FALLBACK_RAND_1989) && \
|
||||
((FALLBACK_RAND_1989) > 0))
|
||||
#if defined(__linux__)
|
||||
#if defined(HAVE_GETRANDOM) || \
|
||||
defined(HAVE_GETRANDOM_SYSCALL)
|
||||
int
|
||||
fallback_rand_getrandom(void *buf, unsigned long len)
|
||||
{
|
||||
unsigned long off = 0;
|
||||
long rval = -1;
|
||||
|
||||
if (!len)
|
||||
return -1;
|
||||
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
#if defined(HAVE_GETRANDOM) || \
|
||||
defined(HAVE_GETRANDOM_SYSCALL)
|
||||
|
||||
while (off < len) {
|
||||
|
||||
#if defined(HAVE_GETRANDOM)
|
||||
rval = (long)getrandom((char *)buf + off, len - off, 0);
|
||||
#elif defined(HAVE_GETRANDOM_SYSCALL)
|
||||
rval = (long)syscall(SYS_getrandom,
|
||||
(char *)buf + off, len - off, 0);
|
||||
#endif
|
||||
|
||||
if (rval < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
return -1; /* unsupported by kernel */
|
||||
}
|
||||
|
||||
off += (unsigned long)rval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
(void)buf;
|
||||
(void)len;
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
/* nobody should use this
|
||||
* (not crypto-safe)
|
||||
*/
|
||||
unsigned long
|
||||
fallback_rand_1989(void)
|
||||
{
|
||||
static unsigned long mix = 0;
|
||||
static unsigned long counter = 0;
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
mix ^= (unsigned long)tv.tv_sec
|
||||
^ (unsigned long)tv.tv_usec
|
||||
^ (unsigned long)getpid()
|
||||
^ (unsigned long)&mix
|
||||
^ counter++
|
||||
^ entropy_jitter();
|
||||
|
||||
/*
|
||||
* Stack addresses can vary between
|
||||
* calls, thus increasing entropy.
|
||||
*/
|
||||
mix ^= (unsigned long)&mix;
|
||||
mix ^= (unsigned long)&tv;
|
||||
mix ^= (unsigned long)&counter;
|
||||
|
||||
return mix;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
entropy_jitter(void)
|
||||
{
|
||||
unsigned long mix;
|
||||
|
||||
struct timeval a, b;
|
||||
long mix_diff;
|
||||
|
||||
int c;
|
||||
|
||||
mix = 0;
|
||||
|
||||
gettimeofday(&a, NULL);
|
||||
|
||||
for (c = 0; c < 32; c++) {
|
||||
|
||||
getpid();
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
check_bin(unsigned long a, const char *a_name)
|
||||
{
|
||||
if (a > 1)
|
||||
err(EINVAL, "%s must be 0 or 1, but is %lu",
|
||||
a_name, (unsigned long)a);
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* String functions
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../include/common.h"
|
||||
|
||||
/* Portable strncmp() that blocks
|
||||
* NULL/empty/unterminated strings
|
||||
*/
|
||||
|
||||
int
|
||||
xstrxcmp(const char *a, const char *b, unsigned long maxlen)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
if (a == NULL || b == NULL)
|
||||
err(EINVAL, "NULL input to xstrxcmp");
|
||||
|
||||
if (*a == '\0' || *b == '\0')
|
||||
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];
|
||||
|
||||
if (ac == '\0' || bc == '\0') {
|
||||
if (ac == bc)
|
||||
return 0;
|
||||
return ac - bc;
|
||||
}
|
||||
|
||||
if (ac != bc)
|
||||
return ac - bc;
|
||||
}
|
||||
|
||||
err(EINVAL, "Unterminated string in xstrxcmp");
|
||||
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Portable strncmp() that blocks
|
||||
* NULL/empty/unterminated strings
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
xstrxlen(const char *scmp, unsigned long maxlen)
|
||||
{
|
||||
unsigned long xstr_index;
|
||||
|
||||
if (scmp == NULL)
|
||||
err(EINVAL, "NULL input to xstrxlen");
|
||||
|
||||
if (*scmp == '\0')
|
||||
err(EINVAL, "Empty string in xstrxlen");
|
||||
|
||||
for (xstr_index = 0;
|
||||
xstr_index < maxlen && scmp[xstr_index] != '\0';
|
||||
xstr_index++);
|
||||
|
||||
if (xstr_index == maxlen)
|
||||
err(EINVAL, "Unterminated string in xstrxlen");
|
||||
|
||||
return xstr_index;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
* Copyright (c) 2022-2026 Leah Rowe <leah@libreboot.org>
|
||||
*
|
||||
* This tool lets you modify Intel GbE NVM (Gigabit Ethernet
|
||||
* Non-Volatile Memory) images, e.g. change the MAC address.
|
||||
* These images configure your Intel Gigabit Ethernet adapter.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "include/common.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct xstate *x = xstatus(argc, argv);
|
||||
struct commands *cmd = &x->cmd[x->i];
|
||||
struct xfile *f = &x->f;
|
||||
|
||||
unsigned long c;
|
||||
|
||||
if (cmd->run == NULL)
|
||||
err(errno, "Command not set");
|
||||
|
||||
cmd->run();
|
||||
|
||||
for (c = 0; c < items(x->cmd); c++)
|
||||
x->cmd[c].run = cmd_helper_err;
|
||||
|
||||
if ((cmd->flags & O_ACCMODE) == O_RDWR)
|
||||
write_to_gbe_bin();
|
||||
|
||||
if (exit_cleanup() == -1)
|
||||
err(EIO, "%s: close", f->fname);
|
||||
|
||||
if (f->io_err_gbe_bin)
|
||||
err(EIO, "%s: error writing final file");
|
||||
|
||||
if (f->tname != NULL)
|
||||
free(f->tname);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user