mirror of
https://codeberg.org/libreboot/lbmk.git
synced 2026-03-25 13:29:03 +02:00
Compare commits
13 Commits
217ad55bed
...
f06db344ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f06db344ad | ||
|
|
3ddd7a0d36 | ||
|
|
2cee58188c | ||
|
|
1ed2ca6b69 | ||
|
|
4fc4946f3c | ||
|
|
f8d9c51a36 | ||
|
|
b8a045ef86 | ||
|
|
715723c7ce | ||
|
|
b16bb6c445 | ||
|
|
75f03ea696 | ||
|
|
88ff5f7380 | ||
|
|
bf5a3df796 | ||
|
|
3522a23587 |
@@ -287,6 +287,11 @@ struct xfile {
|
|||||||
unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
|
unsigned char bufcmp[GBE_BUF_SIZE]; /* compare gbe/tmp/reads */
|
||||||
|
|
||||||
unsigned char pad[GBE_WORK_SIZE]; /* the file that wouldn't die */
|
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
|
/* Command table, MAC address, files
|
||||||
@@ -489,17 +494,22 @@ int try_err(int loop_err, int errval);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void usage(void);
|
void usage(void);
|
||||||
void err_no_cleanup(int nvm_errval, const char *msg, ...);
|
void err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...);
|
||||||
void err(int nvm_errval, const char *msg, ...);
|
void b0rk(int nvm_errval, const char *msg, ...);
|
||||||
int exit_cleanup(void);
|
int exit_cleanup(void);
|
||||||
const char *getnvmprogname(void);
|
const char *getnvmprogname(void);
|
||||||
|
|
||||||
|
void err_mkhtemp(int stfu, int errval, const char *msg, ...);
|
||||||
|
|
||||||
/* libc hardening
|
/* libc hardening
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int new_tmpfile(int *fd, char **path, char *tmpdir);
|
int new_tmpfile(int *fd, char **path, char *tmpdir,
|
||||||
int new_tmpdir(int *fd, char **path, char *tmpdir);
|
const char *template);
|
||||||
int new_tmp_common(int *fd, char **path, int type, char *tmpdir);
|
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,
|
int mkhtemp_try_create(int dirfd,
|
||||||
struct stat *st_dir_initial,
|
struct stat *st_dir_initial,
|
||||||
char *fname_copy,
|
char *fname_copy,
|
||||||
@@ -508,6 +518,14 @@ int mkhtemp_try_create(int dirfd,
|
|||||||
int *fd,
|
int *fd,
|
||||||
struct stat *st,
|
struct stat *st,
|
||||||
int type);
|
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,
|
int mkhtemp(int *fd, struct stat *st,
|
||||||
char *template, int dirfd, const char *fname,
|
char *template, int dirfd, const char *fname,
|
||||||
struct stat *st_dir_initial, int type);
|
struct stat *st_dir_initial, int type);
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ read_checksums(void)
|
|||||||
if (_num_invalid >= _max_invalid) {
|
if (_num_invalid >= _max_invalid) {
|
||||||
|
|
||||||
if (_max_invalid == 1)
|
if (_max_invalid == 1)
|
||||||
err(ECANCELED, "%s: part %lu has a bad checksum",
|
b0rk(ECANCELED, "%s: part %lu has a bad checksum",
|
||||||
f->fname, (size_t)f->part);
|
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);
|
f->fname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,27 +46,27 @@ sanitize_command_index(size_t c)
|
|||||||
check_command_num(c);
|
check_command_num(c);
|
||||||
|
|
||||||
if (cmd->argc < 3)
|
if (cmd->argc < 3)
|
||||||
err(EINVAL, "cmd index %lu: argc below 3, %d",
|
b0rk(EINVAL, "cmd index %lu: argc below 3, %d",
|
||||||
(size_t)c, cmd->argc);
|
(size_t)c, cmd->argc);
|
||||||
|
|
||||||
if (cmd->str == NULL)
|
if (cmd->str == NULL)
|
||||||
err(EINVAL, "cmd index %lu: NULL str",
|
b0rk(EINVAL, "cmd index %lu: NULL str",
|
||||||
(size_t)c);
|
(size_t)c);
|
||||||
|
|
||||||
if (*cmd->str == '\0')
|
if (*cmd->str == '\0')
|
||||||
err(EINVAL, "cmd index %lu: empty str",
|
b0rk(EINVAL, "cmd index %lu: empty str",
|
||||||
(size_t)c);
|
(size_t)c);
|
||||||
|
|
||||||
if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
|
if (slen(cmd->str, MAX_CMD_LEN +1, &rval) < 0)
|
||||||
err(errno, "Could not get command length");
|
b0rk(errno, "Could not get command length");
|
||||||
|
|
||||||
if (rval > MAX_CMD_LEN) {
|
if (rval > MAX_CMD_LEN) {
|
||||||
err(EINVAL, "cmd index %lu: str too long: %s",
|
b0rk(EINVAL, "cmd index %lu: str too long: %s",
|
||||||
(size_t)c, cmd->str);
|
(size_t)c, cmd->str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd->run == NULL)
|
if (cmd->run == NULL)
|
||||||
err(EINVAL, "cmd index %lu: cmd ptr null",
|
b0rk(EINVAL, "cmd index %lu: cmd ptr null",
|
||||||
(size_t)c);
|
(size_t)c);
|
||||||
|
|
||||||
check_bin(cmd->arg_part, "cmd.arg_part");
|
check_bin(cmd->arg_part, "cmd.arg_part");
|
||||||
@@ -80,19 +80,19 @@ sanitize_command_index(size_t c)
|
|||||||
case NVM_SIZE:
|
case NVM_SIZE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
err(EINVAL, "Unsupported rw_size: %lu",
|
b0rk(EINVAL, "Unsupported rw_size: %lu",
|
||||||
(size_t)gbe_rw_size);
|
(size_t)gbe_rw_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gbe_rw_size > GBE_PART_SIZE)
|
if (gbe_rw_size > GBE_PART_SIZE)
|
||||||
err(EINVAL, "rw_size larger than GbE part: %lu",
|
b0rk(EINVAL, "rw_size larger than GbE part: %lu",
|
||||||
(size_t)gbe_rw_size);
|
(size_t)gbe_rw_size);
|
||||||
|
|
||||||
_flag = (cmd->flags & O_ACCMODE);
|
_flag = (cmd->flags & O_ACCMODE);
|
||||||
|
|
||||||
if (_flag != O_RDONLY &&
|
if (_flag != O_RDONLY &&
|
||||||
_flag != O_RDWR)
|
_flag != O_RDWR)
|
||||||
err(EINVAL, "invalid cmd.flags setting");
|
b0rk(EINVAL, "invalid cmd.flags setting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -110,7 +110,7 @@ set_cmd(int argc, char *argv[])
|
|||||||
cmd = x->cmd[c].str;
|
cmd = x->cmd[c].str;
|
||||||
|
|
||||||
if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
|
if (scmp(argv[2], cmd, MAX_CMD_LEN, &rval) < 0)
|
||||||
err_no_cleanup(EINVAL,
|
err_no_cleanup(0, EINVAL,
|
||||||
"could not compare command strings");
|
"could not compare command strings");
|
||||||
if (rval != 0)
|
if (rval != 0)
|
||||||
continue; /* not the right command */
|
continue; /* not the right command */
|
||||||
@@ -123,7 +123,7 @@ set_cmd(int argc, char *argv[])
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err_no_cleanup(EINVAL,
|
err_no_cleanup(0, EINVAL,
|
||||||
"Too few args on command '%s'", cmd);
|
"Too few args on command '%s'", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,11 +148,11 @@ set_cmd_args(int argc, char *argv[])
|
|||||||
/* Maintainer bug
|
/* Maintainer bug
|
||||||
*/
|
*/
|
||||||
if (cmd->arg_part && argc < 4)
|
if (cmd->arg_part && argc < 4)
|
||||||
err(EINVAL,
|
b0rk(EINVAL,
|
||||||
"arg_part set for command that needs argc4");
|
"arg_part set for command that needs argc4");
|
||||||
|
|
||||||
if (cmd->arg_part && i == CMD_SETMAC)
|
if (cmd->arg_part && i == CMD_SETMAC)
|
||||||
err(EINVAL,
|
b0rk(EINVAL,
|
||||||
"arg_part set on CMD_SETMAC");
|
"arg_part set on CMD_SETMAC");
|
||||||
|
|
||||||
if (i == CMD_SETMAC) {
|
if (i == CMD_SETMAC) {
|
||||||
@@ -174,13 +174,13 @@ conv_argv_part_num(const char *part_str)
|
|||||||
unsigned char ch;
|
unsigned char ch;
|
||||||
|
|
||||||
if (part_str[0] == '\0' || part_str[1] != '\0')
|
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
|
/* char signedness is implementation-defined
|
||||||
*/
|
*/
|
||||||
ch = (unsigned char)part_str[0];
|
ch = (unsigned char)part_str[0];
|
||||||
if (ch < '0' || ch > '1')
|
if (ch < '0' || ch > '1')
|
||||||
err(EINVAL, "Bad part number (%c)", ch);
|
b0rk(EINVAL, "Bad part number (%c)", ch);
|
||||||
|
|
||||||
return (size_t)(ch - '0');
|
return (size_t)(ch - '0');
|
||||||
}
|
}
|
||||||
@@ -189,7 +189,7 @@ void
|
|||||||
check_command_num(size_t c)
|
check_command_num(size_t c)
|
||||||
{
|
{
|
||||||
if (!valid_command(c))
|
if (!valid_command(c))
|
||||||
err(EINVAL, "Invalid run_cmd arg: %lu",
|
b0rk(EINVAL, "Invalid run_cmd arg: %lu",
|
||||||
(size_t)c);
|
(size_t)c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ valid_command(size_t c)
|
|||||||
cmd = &x->cmd[c];
|
cmd = &x->cmd[c];
|
||||||
|
|
||||||
if (c != cmd->chk)
|
if (c != cmd->chk)
|
||||||
err(EINVAL,
|
b0rk(EINVAL,
|
||||||
"Invalid cmd chk value (%lu) vs arg: %lu",
|
"Invalid cmd chk value (%lu) vs arg: %lu",
|
||||||
cmd->chk, c);
|
cmd->chk, c);
|
||||||
|
|
||||||
@@ -240,10 +240,10 @@ parse_mac_string(void)
|
|||||||
size_t rval;
|
size_t rval;
|
||||||
|
|
||||||
if (slen(x->mac.str, 18, &rval) < 0)
|
if (slen(x->mac.str, 18, &rval) < 0)
|
||||||
err(EINVAL, "Could not determine MAC length");
|
b0rk(EINVAL, "Could not determine MAC length");
|
||||||
|
|
||||||
if (rval != 17)
|
if (rval != 17)
|
||||||
err(EINVAL, "MAC address is the wrong length");
|
b0rk(EINVAL, "MAC address is the wrong length");
|
||||||
|
|
||||||
memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
|
memset(mac->mac_buf, 0, sizeof(mac->mac_buf));
|
||||||
|
|
||||||
@@ -251,10 +251,10 @@ parse_mac_string(void)
|
|||||||
set_mac_byte(mac_byte);
|
set_mac_byte(mac_byte);
|
||||||
|
|
||||||
if ((mac->mac_buf[0] | mac->mac_buf[1] | mac->mac_buf[2]) == 0)
|
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)
|
if (mac->mac_buf[0] & 1)
|
||||||
err(EINVAL, "Must not specify multicast MAC address");
|
b0rk(EINVAL, "Must not specify multicast MAC address");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -272,7 +272,7 @@ set_mac_byte(size_t mac_byte_pos)
|
|||||||
|
|
||||||
if (mac_str_pos < 15) {
|
if (mac_str_pos < 15) {
|
||||||
if ((separator = mac->str[mac_str_pos + 2]) != ':')
|
if ((separator = mac->str[mac_str_pos + 2]) != ':')
|
||||||
err(EINVAL, "Invalid MAC address separator '%c'",
|
b0rk(EINVAL, "Invalid MAC address separator '%c'",
|
||||||
separator);
|
separator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,9 +294,9 @@ set_mac_nib(size_t mac_str_pos,
|
|||||||
|
|
||||||
if ((hex_num = hextonum(mac_ch)) > 15) {
|
if ((hex_num = hextonum(mac_ch)) > 15) {
|
||||||
if (hex_num >= 17)
|
if (hex_num >= 17)
|
||||||
err(EIO, "Randomisation failure");
|
b0rk(EIO, "Randomisation failure");
|
||||||
else
|
else
|
||||||
err(EINVAL, "Invalid character '%c'",
|
b0rk(EINVAL, "Invalid character '%c'",
|
||||||
mac->str[mac_str_pos + mac_nib_pos]);
|
mac->str[mac_str_pos + mac_nib_pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +509,7 @@ cat(size_t nff)
|
|||||||
|
|
||||||
if ((size_t)x->cat != nff) {
|
if ((size_t)x->cat != nff) {
|
||||||
|
|
||||||
err(ECANCELED, "erroneous call to cat");
|
b0rk(ECANCELED, "erroneous call to cat");
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(NULL);
|
fflush(NULL);
|
||||||
@@ -532,12 +532,12 @@ void
|
|||||||
cat_buf(unsigned char *b)
|
cat_buf(unsigned char *b)
|
||||||
{
|
{
|
||||||
if (b == NULL)
|
if (b == NULL)
|
||||||
err(errno, "null pointer in cat command");
|
b0rk(errno, "null pointer in cat command");
|
||||||
|
|
||||||
if (rw_file_exact(STDOUT_FILENO, b,
|
if (rw_file_exact(STDOUT_FILENO, b,
|
||||||
GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR,
|
GBE_PART_SIZE, 0, IO_WRITE, LOOP_EAGAIN, LOOP_EINTR,
|
||||||
MAX_ZERO_RW_RETRY, OFF_ERR) < 0)
|
MAX_ZERO_RW_RETRY, OFF_ERR) < 0)
|
||||||
err(errno, "stdout: cat");
|
b0rk(errno, "stdout: cat");
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
check_cmd(void (*fn)(void),
|
check_cmd(void (*fn)(void),
|
||||||
@@ -547,7 +547,7 @@ check_cmd(void (*fn)(void),
|
|||||||
size_t i = x->i;
|
size_t i = x->i;
|
||||||
|
|
||||||
if (x->cmd[i].run != fn)
|
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);
|
name, x->cmd[i].str);
|
||||||
|
|
||||||
/* prevent second command
|
/* prevent second command
|
||||||
@@ -559,6 +559,6 @@ check_cmd(void (*fn)(void),
|
|||||||
void
|
void
|
||||||
cmd_helper_err(void)
|
cmd_helper_err(void)
|
||||||
{
|
{
|
||||||
err(ECANCELED,
|
b0rk(ECANCELED,
|
||||||
"Erroneously running command twice");
|
"Erroneously running command twice");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,16 +96,16 @@ void
|
|||||||
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
|
xopen(int *fd_ptr, const char *path, int flags, struct stat *st)
|
||||||
{
|
{
|
||||||
if ((*fd_ptr = open(path, flags)) < 0)
|
if ((*fd_ptr = open(path, flags)) < 0)
|
||||||
err(errno, "%s", path);
|
err_no_cleanup(0, errno, "%s", path);
|
||||||
|
|
||||||
if (fstat(*fd_ptr, st) < 0)
|
if (fstat(*fd_ptr, st) < 0)
|
||||||
err(errno, "%s: stat", path);
|
err_no_cleanup(0, errno, "%s: stat", path);
|
||||||
|
|
||||||
if (!S_ISREG(st->st_mode))
|
if (!S_ISREG(st->st_mode))
|
||||||
err(errno, "%s: not a regular file", path);
|
err_no_cleanup(0, errno, "%s: not a regular file", path);
|
||||||
|
|
||||||
if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1)
|
if (lseek_on_eintr(*fd_ptr, 0, SEEK_CUR, 1, 1) == (off_t)-1)
|
||||||
err(errno, "%s: file not seekable", path);
|
err_no_cleanup(0, errno, "%s: file not seekable", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fsync() the directory of a file,
|
/* fsync() the directory of a file,
|
||||||
@@ -787,7 +787,12 @@ fs_resolve_at(int dirfd, const char *path, int flags)
|
|||||||
int nextfd = -1;
|
int nextfd = -1;
|
||||||
int curfd;
|
int curfd;
|
||||||
const char *p;
|
const char *p;
|
||||||
char name[256];
|
#if defined(PATH_LEN) && \
|
||||||
|
((PATH_LEN) >= 256)
|
||||||
|
char name[PATH_LEN];
|
||||||
|
#else
|
||||||
|
char name[4096];
|
||||||
|
#endif
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
int r;
|
int r;
|
||||||
int is_last;
|
int is_last;
|
||||||
|
|||||||
@@ -32,16 +32,16 @@ open_gbe_file(void)
|
|||||||
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st);
|
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, &f->gbe_st);
|
||||||
|
|
||||||
if (f->gbe_st.st_nlink > 1)
|
if (f->gbe_st.st_nlink > 1)
|
||||||
err(EINVAL,
|
b0rk(EINVAL,
|
||||||
"%s: warning: file has multiple (%lu) hard links\n",
|
"%s: warning: file has multiple (%lu) hard links\n",
|
||||||
f->fname, (size_t)f->gbe_st.st_nlink);
|
f->fname, (size_t)f->gbe_st.st_nlink);
|
||||||
|
|
||||||
if (f->gbe_st.st_nlink == 0)
|
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);
|
_flags = fcntl(f->gbe_fd, F_GETFL);
|
||||||
if (_flags == -1)
|
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
|
/* O_APPEND allows POSIX write() to ignore
|
||||||
* the current write offset and write at EOF,
|
* the current write offset and write at EOF,
|
||||||
@@ -49,7 +49,7 @@ open_gbe_file(void)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (_flags & O_APPEND)
|
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;
|
f->gbe_file_size = f->gbe_st.st_size;
|
||||||
|
|
||||||
@@ -59,11 +59,11 @@ open_gbe_file(void)
|
|||||||
case SIZE_128KB:
|
case SIZE_128KB:
|
||||||
break;
|
break;
|
||||||
default:
|
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)
|
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
|
void
|
||||||
@@ -98,7 +98,7 @@ read_file(void)
|
|||||||
MAX_ZERO_RW_RETRY, OFF_ERR);
|
MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||||
|
|
||||||
if (_r < 0)
|
if (_r < 0)
|
||||||
err(errno, "%s: read failed", f->fname);
|
b0rk(errno, "%s: read failed", f->fname);
|
||||||
|
|
||||||
/* copy to tmpfile
|
/* copy to tmpfile
|
||||||
*/
|
*/
|
||||||
@@ -107,34 +107,34 @@ read_file(void)
|
|||||||
MAX_ZERO_RW_RETRY, OFF_ERR);
|
MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||||
|
|
||||||
if (_r < 0)
|
if (_r < 0)
|
||||||
err(errno, "%s: %s: copy failed",
|
b0rk(errno, "%s: %s: copy failed",
|
||||||
f->fname, f->tname);
|
f->fname, f->tname);
|
||||||
|
|
||||||
/* file size comparison
|
/* file size comparison
|
||||||
*/
|
*/
|
||||||
if (fstat(f->tmp_fd, &_st) == -1)
|
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;
|
f->gbe_tmp_size = _st.st_size;
|
||||||
|
|
||||||
if (f->gbe_tmp_size != f->gbe_file_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);
|
f->fname, f->tname);
|
||||||
|
|
||||||
/* needs sync, for verification
|
/* needs sync, for verification
|
||||||
*/
|
*/
|
||||||
if (fsync_on_eintr(f->tmp_fd) == -1)
|
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,
|
_r = rw_file_exact(f->tmp_fd, f->bufcmp, f->gbe_file_size,
|
||||||
0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR,
|
0, IO_PREAD, NO_LOOP_EAGAIN, LOOP_EINTR,
|
||||||
MAX_ZERO_RW_RETRY, OFF_ERR);
|
MAX_ZERO_RW_RETRY, OFF_ERR);
|
||||||
|
|
||||||
if (_r < 0)
|
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)
|
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);
|
f->fname, f->tname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +152,10 @@ write_gbe_file(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
|
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)
|
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;
|
update_checksum = cmd->chksum_write;
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ rw_gbe_file_part(size_t p, int rw_type,
|
|||||||
gbe_rw_size = cmd->rw_size;
|
gbe_rw_size = cmd->rw_size;
|
||||||
|
|
||||||
if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
|
if (rw_type < IO_PREAD || rw_type > IO_PWRITE)
|
||||||
err(errno, "%s: %s: part %lu: invalid rw_type, %d",
|
b0rk(errno, "%s: %s: part %lu: invalid rw_type, %d",
|
||||||
f->fname, rw_type_str, (size_t)p, rw_type);
|
f->fname, rw_type_str, (size_t)p, rw_type);
|
||||||
|
|
||||||
mem_offset = gbe_mem_offset(p, rw_type_str);
|
mem_offset = gbe_mem_offset(p, rw_type_str);
|
||||||
@@ -198,11 +198,11 @@ rw_gbe_file_part(size_t p, int rw_type,
|
|||||||
gbe_rw_size, file_offset, rw_type);
|
gbe_rw_size, file_offset, rw_type);
|
||||||
|
|
||||||
if (rval == -1)
|
if (rval == -1)
|
||||||
err(errno, "%s: %s: part %lu",
|
b0rk(errno, "%s: %s: part %lu",
|
||||||
f->fname, rw_type_str, (size_t)p);
|
f->fname, rw_type_str, (size_t)p);
|
||||||
|
|
||||||
if ((size_t)rval != gbe_rw_size)
|
if ((size_t)rval != gbe_rw_size)
|
||||||
err(EIO, "%s: partial %s: part %lu",
|
b0rk(EIO, "%s: partial %s: part %lu",
|
||||||
f->fname, rw_type_str, (size_t)p);
|
f->fname, rw_type_str, (size_t)p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +226,7 @@ write_to_gbe_bin(void)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (fsync_on_eintr(f->tmp_fd) == -1)
|
if (fsync_on_eintr(f->tmp_fd) == -1)
|
||||||
err(errno, "%s: fsync (pre-verification)",
|
b0rk(errno, "%s: fsync (pre-verification)",
|
||||||
f->tname);
|
f->tname);
|
||||||
|
|
||||||
check_written_part(0);
|
check_written_part(0);
|
||||||
@@ -235,7 +235,7 @@ write_to_gbe_bin(void)
|
|||||||
report_io_err_rw();
|
report_io_err_rw();
|
||||||
|
|
||||||
if (f->io_err_gbe)
|
if (f->io_err_gbe)
|
||||||
err(EIO, "%s: bad write", f->fname);
|
b0rk(EIO, "%s: bad write", f->fname);
|
||||||
|
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
|
|
||||||
@@ -307,10 +307,10 @@ check_written_part(size_t p)
|
|||||||
memset(f->pad, 0xff, sizeof(f->pad));
|
memset(f->pad, 0xff, sizeof(f->pad));
|
||||||
|
|
||||||
if (same_file(f->tmp_fd, &f->tmp_st, 0) < 0)
|
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)
|
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,
|
rval = rw_gbe_file_exact(f->tmp_fd, f->pad,
|
||||||
gbe_rw_size, file_offset, IO_PREAD);
|
gbe_rw_size, file_offset, IO_PREAD);
|
||||||
@@ -432,32 +432,12 @@ gbe_mv(void)
|
|||||||
|
|
||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
|
|
||||||
/* TODO: remove this path-based rename,
|
rval = fs_rename_at(f->dirfd, f->tmpbase,
|
||||||
use fd */
|
f->dirfd, f->base);
|
||||||
rval = rename(f->tname, f->fname);
|
|
||||||
|
|
||||||
if (rval > -1) {
|
|
||||||
|
|
||||||
/* rename on same filesystem
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
if (rval > -1)
|
||||||
tmp_gbe_bin_exists = 0;
|
tmp_gbe_bin_exists = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
if (fsync(dest_fd) < 0) {
|
|
||||||
f->io_err_gbe_bin = 1;
|
|
||||||
rval = -1;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
goto ret_gbe_mv;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errno != EXDEV)
|
|
||||||
goto ret_gbe_mv;
|
|
||||||
|
|
||||||
err(errno, "BUG: cross-filesystem move (this shouldn't happen)");
|
|
||||||
|
|
||||||
ret_gbe_mv:
|
ret_gbe_mv:
|
||||||
|
|
||||||
/* TODO: this whole section is bloat.
|
/* TODO: this whole section is bloat.
|
||||||
@@ -560,11 +540,11 @@ gbe_x_offset(size_t p, const char *f_op, const char *d_type,
|
|||||||
off = ((off_t)p) * (off_t)nsize;
|
off = ((off_t)p) * (off_t)nsize;
|
||||||
|
|
||||||
if (off > ncmp - GBE_PART_SIZE)
|
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);
|
f->fname, d_type, f_op);
|
||||||
|
|
||||||
if (off != 0 && off != ncmp >> 1)
|
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);
|
f->fname, d_type, f_op);
|
||||||
|
|
||||||
return off;
|
return off;
|
||||||
|
|||||||
@@ -19,26 +19,36 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/* for openat2: */
|
/* for openat2 / fast path: */
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <linux/openat2.h>
|
#include <linux/openat2.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
#ifndef O_TMPFILE
|
||||||
|
#define O_TMPFILE 020000000
|
||||||
|
#endif
|
||||||
|
#ifndef AT_EMPTY_PATH
|
||||||
|
#define AT_EMPTY_PATH 0x1000
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../include/common.h"
|
#include "../include/common.h"
|
||||||
|
|
||||||
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
||||||
int
|
int
|
||||||
new_tmpfile(int *fd, char **path, char *tmpdir)
|
new_tmpfile(int *fd, char **path, char *tmpdir,
|
||||||
|
const char *template)
|
||||||
{
|
{
|
||||||
return new_tmp_common(fd, path, MKHTEMP_FILE, tmpdir);
|
return new_tmp_common(fd, path, MKHTEMP_FILE,
|
||||||
|
tmpdir, template);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
||||||
int
|
int
|
||||||
new_tmpdir(int *fd, char **path, char *tmpdir)
|
new_tmpdir(int *fd, char **path, char *tmpdir,
|
||||||
|
const char *template)
|
||||||
{
|
{
|
||||||
return new_tmp_common(fd, path, MKHTEMP_DIR, tmpdir);
|
return new_tmp_common(fd, path, MKHTEMP_DIR,
|
||||||
|
tmpdir, template);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
/* note: tmpdir is an override of TMPDIR or /tmp or /var/tmp */
|
||||||
@@ -59,7 +69,7 @@ new_tmpdir(int *fd, char **path, char *tmpdir)
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
new_tmp_common(int *fd, char **path, int type,
|
new_tmp_common(int *fd, char **path, int type,
|
||||||
char *tmpdir)
|
char *tmpdir, const char *template)
|
||||||
{
|
{
|
||||||
#if defined(PATH_LEN) && \
|
#if defined(PATH_LEN) && \
|
||||||
(PATH_LEN) >= 256
|
(PATH_LEN) >= 256
|
||||||
@@ -69,7 +79,8 @@ new_tmp_common(int *fd, char **path, int type,
|
|||||||
#endif
|
#endif
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
char suffix[] = "tmp.XXXXXXXXXX";
|
const char *suffix;
|
||||||
|
size_t suffix_len;
|
||||||
|
|
||||||
size_t dirlen;
|
size_t dirlen;
|
||||||
size_t destlen;
|
size_t destlen;
|
||||||
@@ -132,10 +143,18 @@ new_tmp_common(int *fd, char **path, int type,
|
|||||||
if (*tmpdir != '/')
|
if (*tmpdir != '/')
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
if (template != NULL)
|
||||||
|
suffix = template;
|
||||||
|
else
|
||||||
|
suffix = "tmp.XXXXXXXXXX";
|
||||||
|
|
||||||
|
if (slen(suffix, maxlen, &suffix_len) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* sizeof adds an extra byte, useful
|
/* sizeof adds an extra byte, useful
|
||||||
* because we also want '.' or '/'
|
* because we also want '.' or '/'
|
||||||
*/
|
*/
|
||||||
destlen = dirlen + sizeof(suffix);
|
destlen = dirlen + 1 + suffix_len;
|
||||||
if (destlen > maxlen - 1) {
|
if (destlen > maxlen - 1) {
|
||||||
errno = EOVERFLOW;
|
errno = EOVERFLOW;
|
||||||
goto err;
|
goto err;
|
||||||
@@ -149,7 +168,7 @@ new_tmp_common(int *fd, char **path, int type,
|
|||||||
|
|
||||||
memcpy(dest, tmpdir, dirlen);
|
memcpy(dest, tmpdir, dirlen);
|
||||||
*(dest + dirlen) = '/';
|
*(dest + dirlen) = '/';
|
||||||
memcpy(dest + dirlen + 1, suffix, sizeof(suffix) - 1);
|
memcpy(dest + dirlen + 1, suffix, suffix_len);
|
||||||
*(dest + destlen) = '\0';
|
*(dest + destlen) = '\0';
|
||||||
|
|
||||||
fname = dest + dirlen + 1;
|
fname = dest + dirlen + 1;
|
||||||
@@ -427,11 +446,23 @@ world_writeable_and_sticky(
|
|||||||
/* must be fully executable
|
/* must be fully executable
|
||||||
* by everyone, or openat2
|
* by everyone, or openat2
|
||||||
* becomes unreliable**
|
* becomes unreliable**
|
||||||
|
*
|
||||||
|
* TODO: loosen these, as a toggle.
|
||||||
|
* execution rights isn't
|
||||||
|
* really a requirement for
|
||||||
|
* TMPDIR, except maybe search,
|
||||||
|
* but this function will be
|
||||||
|
* generalised at some point
|
||||||
|
* for use in other tools
|
||||||
|
* besides just mkhtemp.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
if (!(st.st_mode & S_IXUSR) ||
|
if (!(st.st_mode & S_IXUSR) ||
|
||||||
!(st.st_mode & S_IXGRP) ||
|
!(st.st_mode & S_IXGRP) ||
|
||||||
!(st.st_mode & S_IXOTH)) {
|
!(st.st_mode & S_IXOTH)) {
|
||||||
|
*/
|
||||||
|
/* just require it for *you*, for now */
|
||||||
|
if (!(st.st_mode & S_IXUSR)) {
|
||||||
errno = EACCES;
|
errno = EACCES;
|
||||||
goto sticky_hell;
|
goto sticky_hell;
|
||||||
}
|
}
|
||||||
@@ -457,6 +488,12 @@ world_writeable_and_sticky(
|
|||||||
goto sticky_hell; /* not sticky */
|
goto sticky_hell; /* not sticky */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if anyone even looks at you funny, drop
|
||||||
|
* everything on the floor and refuse to function
|
||||||
|
*/
|
||||||
|
if (faccessat(dirfd, ".", X_OK, AT_EACCESS) < 0)
|
||||||
|
goto sticky_hell;
|
||||||
|
|
||||||
/* non-world-writeable, so
|
/* non-world-writeable, so
|
||||||
* stickiness is do-not-care
|
* stickiness is do-not-care
|
||||||
*/
|
*/
|
||||||
@@ -567,7 +604,7 @@ mkhtemp(int *fd,
|
|||||||
if_err(len >= max_len, EMSGSIZE)
|
if_err(len >= max_len, EMSGSIZE)
|
||||||
||
|
||
|
||||||
if_err_sys(slen(fname, max_len, &fname_len)) ||
|
if_err_sys(slen(fname, max_len, &fname_len)) ||
|
||||||
if_err(fname == 0, EINVAL) ||
|
if_err(fname == NULL, EINVAL) ||
|
||||||
if_err(strrchr(fname, '/') != NULL, EINVAL))
|
if_err(strrchr(fname, '/') != NULL, EINVAL))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -655,6 +692,18 @@ mkhtemp_try_create(int dirfd,
|
|||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (type == MKHTEMP_FILE) {
|
if (type == MKHTEMP_FILE) {
|
||||||
|
#ifdef __linux__
|
||||||
|
/* try O_TMPFILE fast path */
|
||||||
|
if (mkhtemp_tmpfile_linux(dirfd,
|
||||||
|
st_dir_initial, fname_copy,
|
||||||
|
p, xc, fd, st) == 0) {
|
||||||
|
|
||||||
|
errno = saved_errno;
|
||||||
|
rval = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
*fd = openat2p(dirfd, fname_copy,
|
*fd = openat2p(dirfd, fname_copy,
|
||||||
O_RDWR | O_CREAT | O_EXCL |
|
O_RDWR | O_CREAT | O_EXCL |
|
||||||
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, 0600);
|
O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, 0600);
|
||||||
@@ -746,6 +795,94 @@ out:
|
|||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* linux has its own special hardening
|
||||||
|
available specifically for tmpfiles,
|
||||||
|
which eliminates many race conditions.
|
||||||
|
|
||||||
|
we still use openat() on bsd, which is
|
||||||
|
still ok with our other mitigations
|
||||||
|
*/
|
||||||
|
#ifdef __linux__
|
||||||
|
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 saved_errno = errno;
|
||||||
|
int tmpfd = -1;
|
||||||
|
size_t retries;
|
||||||
|
int linked = 0;
|
||||||
|
|
||||||
|
if (fd == NULL || st == NULL ||
|
||||||
|
fname_copy == NULL || p == NULL ||
|
||||||
|
st_dir_initial == NULL) {
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create unnamed tmpfile */
|
||||||
|
tmpfd = openat(dirfd, ".",
|
||||||
|
O_TMPFILE | O_RDWR | O_CLOEXEC, 0600);
|
||||||
|
|
||||||
|
if (tmpfd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
for (retries = 0; retries < MKHTEMP_RETRY_MAX; retries++) {
|
||||||
|
|
||||||
|
if (mkhtemp_fill_random(p, xc) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (fd_verify_dir_identity(dirfd,
|
||||||
|
st_dir_initial) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (linkat(tmpfd, "",
|
||||||
|
dirfd, fname_copy,
|
||||||
|
AT_EMPTY_PATH) == 0) {
|
||||||
|
|
||||||
|
linked = 1; /* file created */
|
||||||
|
|
||||||
|
if (fd_verify_dir_identity(dirfd, st_dir_initial) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
*fd = tmpfd;
|
||||||
|
|
||||||
|
if (fstat(*fd, st) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (secure_file(fd, st, st,
|
||||||
|
O_APPEND, 1, 1, 0600) < 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
errno = saved_errno;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != EEXIST)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* retry on collision */
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = EEXIST;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (linked)
|
||||||
|
(void) unlinkat(dirfd, fname_copy, 0);
|
||||||
|
|
||||||
|
close_no_err(&tmpfd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int
|
int
|
||||||
mkhtemp_fill_random(char *p, size_t xc)
|
mkhtemp_fill_random(char *p, size_t xc)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -436,6 +436,6 @@ void
|
|||||||
check_bin(size_t a, const char *a_name)
|
check_bin(size_t a, const char *a_name)
|
||||||
{
|
{
|
||||||
if (a > 1)
|
if (a > 1)
|
||||||
err(EINVAL, "%s must be 0 or 1, but is %lu",
|
err_no_cleanup(0, EINVAL, "%s must be 0 or 1, but is %lu",
|
||||||
a_name, (size_t)a);
|
a_name, (size_t)a);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ xstart(int argc, char *argv[])
|
|||||||
static char *dir = NULL;
|
static char *dir = NULL;
|
||||||
static char *base = NULL;
|
static char *base = NULL;
|
||||||
char *realdir = NULL;
|
char *realdir = NULL;
|
||||||
|
char *tmpdir = NULL;
|
||||||
|
char *tmpbase_local = NULL;
|
||||||
|
|
||||||
static struct xstate us = {
|
static struct xstate us = {
|
||||||
{
|
{
|
||||||
@@ -96,9 +98,9 @@ xstart(int argc, char *argv[])
|
|||||||
return &us;
|
return &us;
|
||||||
|
|
||||||
if (argc < 3)
|
if (argc < 3)
|
||||||
err_no_cleanup(EINVAL, "xstart: Too few arguments");
|
err_no_cleanup(0, EINVAL, "xstart: Too few arguments");
|
||||||
if (argv == NULL)
|
if (argv == NULL)
|
||||||
err_no_cleanup(EINVAL, "xstart: NULL argv");
|
err_no_cleanup(0, EINVAL, "xstart: NULL argv");
|
||||||
|
|
||||||
first_run = 0;
|
first_run = 0;
|
||||||
|
|
||||||
@@ -111,23 +113,41 @@ xstart(int argc, char *argv[])
|
|||||||
us.f.tname = NULL;
|
us.f.tname = NULL;
|
||||||
|
|
||||||
if ((realdir = realpath(us.f.fname, NULL)) == NULL)
|
if ((realdir = realpath(us.f.fname, NULL)) == NULL)
|
||||||
err_no_cleanup(errno, "xstart: can't get realpath of %s",
|
err_no_cleanup(0, errno, "xstart: can't get realpath of %s",
|
||||||
us.f.fname);
|
us.f.fname);
|
||||||
|
|
||||||
if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
|
if (fs_dirname_basename(realdir, &dir, &base, 0) < 0)
|
||||||
err_no_cleanup(errno, "xstart: don't know CWD of %s",
|
err_no_cleanup(0, errno, "xstart: don't know CWD of %s",
|
||||||
us.f.fname);
|
us.f.fname);
|
||||||
|
|
||||||
if (new_tmpfile(&us.f.tmp_fd, &us.f.tname, dir) < 0)
|
if ((us.f.base = strdup(base)) == NULL)
|
||||||
err_no_cleanup(errno, "%s", us.f.tname);
|
err_no_cleanup(0, errno, "strdup base");
|
||||||
|
|
||||||
|
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 (us.f.tname == NULL)
|
if (us.f.tname == NULL)
|
||||||
err_no_cleanup(errno, "x->f.tname null");
|
err_no_cleanup(0, errno, "x->f.tname null");
|
||||||
if (*us.f.tname == '\0')
|
if (*us.f.tname == '\0')
|
||||||
err_no_cleanup(errno, "x->f.tname empty");
|
err_no_cleanup(0, errno, "x->f.tname empty");
|
||||||
|
|
||||||
if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
|
if (fstat(us.f.tmp_fd, &us.f.tmp_st) < 0)
|
||||||
err_no_cleanup(errno, "%s: stat", us.f.tname);
|
err_no_cleanup(0, errno, "%s: stat", us.f.tname);
|
||||||
|
|
||||||
memset(us.f.real_buf, 0, sizeof(us.f.real_buf));
|
memset(us.f.real_buf, 0, sizeof(us.f.real_buf));
|
||||||
memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp));
|
memset(us.f.bufcmp, 0, sizeof(us.f.bufcmp));
|
||||||
@@ -144,13 +164,13 @@ xstatus(void)
|
|||||||
struct xstate *x = xstart(0, NULL);
|
struct xstate *x = xstart(0, NULL);
|
||||||
|
|
||||||
if (x == NULL)
|
if (x == NULL)
|
||||||
err_no_cleanup(EACCES, "NULL pointer to xstate");
|
err_no_cleanup(0, EACCES, "NULL pointer to xstate");
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
err(int nvm_errval, const char *msg, ...)
|
b0rk(int nvm_errval, const char *msg, ...)
|
||||||
{
|
{
|
||||||
struct xstate *x = xstatus();
|
struct xstate *x = xstatus();
|
||||||
|
|
||||||
@@ -197,6 +217,10 @@ exit_cleanup(void)
|
|||||||
if (f->tname != NULL)
|
if (f->tname != NULL)
|
||||||
if (unlink(f->tname) == -1)
|
if (unlink(f->tname) == -1)
|
||||||
close_err = 1;
|
close_err = 1;
|
||||||
|
|
||||||
|
close_no_err(&f->dirfd);
|
||||||
|
free_if_null(&f->base);
|
||||||
|
free_if_null(&f->tmpbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saved_errno)
|
if (saved_errno)
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ slen(const char *s,
|
|||||||
/* the one for nvmutil state is in state.c */
|
/* the one for nvmutil state is in state.c */
|
||||||
/* this one just exits */
|
/* this one just exits */
|
||||||
void
|
void
|
||||||
err_no_cleanup(int nvm_errval, const char *msg, ...)
|
err_no_cleanup(int stfu, int nvm_errval, const char *msg, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
int saved_errno = errno;
|
int saved_errno = errno;
|
||||||
@@ -141,7 +141,10 @@ err_no_cleanup(int nvm_errval, const char *msg, ...)
|
|||||||
vfprintf(stderr, msg, args);
|
vfprintf(stderr, msg, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
fprintf(stderr, ": %s\n", strerror(errno));
|
if (p != NULL)
|
||||||
|
fprintf(stderr, ": %s\n", strerror(errno));
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s\n", strerror(errno));
|
||||||
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,5 @@ usage(void)
|
|||||||
util, util, util, util,
|
util, util, util, util,
|
||||||
util, util, util);
|
util, util, util);
|
||||||
|
|
||||||
err(EINVAL, "Too few arguments");
|
b0rk(EINVAL, "Too few arguments");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,6 @@ check_nvm_bound(size_t c, size_t p)
|
|||||||
check_bin(p, "part number");
|
check_bin(p, "part number");
|
||||||
|
|
||||||
if (c >= NVM_WORDS)
|
if (c >= NVM_WORDS)
|
||||||
err(ECANCELED, "check_nvm_bound: out of bounds %lu",
|
b0rk(ECANCELED, "check_nvm_bound: out of bounds %lu",
|
||||||
(size_t)c);
|
(size_t)c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
* generally provides much higher strictness than previous
|
* generally provides much higher strictness than previous
|
||||||
* implementations such as mktemp, mkstemp or even mkdtemp.
|
* 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
|
* Many programs rely on mktemp, and they use TMPDIR in a way
|
||||||
* that is quite insecure. Mkhtemp intends to change that,
|
* that is quite insecure. Mkhtemp intends to change that,
|
||||||
* quite dramatically, with: userspace sandbox (and use OS
|
* quite dramatically, with: userspace sandbox (and use OS
|
||||||
@@ -67,67 +71,120 @@
|
|||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char *s = NULL;
|
|
||||||
int fd = -1;
|
|
||||||
char c;
|
|
||||||
int type = MKHTEMP_FILE;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
#if defined (PATH_LEN) && \
|
#if defined (PATH_LEN) && \
|
||||||
(PATH_LEN) >= 256
|
(PATH_LEN) >= 256
|
||||||
size_t maxlen = PATH_LEN;
|
size_t maxlen = PATH_LEN;
|
||||||
#else
|
#else
|
||||||
size_t maxlen = 4096;
|
size_t maxlen = 4096;
|
||||||
#endif
|
#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)
|
if (lbgetprogname(argv[0]) == NULL)
|
||||||
err_no_cleanup(errno, "could not set progname");
|
err_no_cleanup(stfu, errno, "could not set progname");
|
||||||
|
|
||||||
/* https://man.openbsd.org/pledge.2 */
|
/* https://man.openbsd.org/pledge.2 */
|
||||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||||
#if (OpenBSD) >= 509
|
#if (OpenBSD) >= 509
|
||||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||||
err_no_cleanup(errno, "pledge, main");
|
goto err_usage;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while ((c =
|
while ((c =
|
||||||
getopt(argc, argv, "d")) != -1) {
|
getopt(argc, argv, "qdp:")) != -1) {
|
||||||
|
|
||||||
switch(c) {
|
switch (c) {
|
||||||
case 'd':
|
case 'd':
|
||||||
|
|
||||||
type = MKHTEMP_DIR;
|
type = MKHTEMP_DIR;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
|
|
||||||
err_no_cleanup(EINVAL,
|
case 'p':
|
||||||
"usage: mkhtemp [-d]\n");
|
tmpdir = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'q': /* don't print errors */
|
||||||
|
/* (exit status unchanged) */
|
||||||
|
stfu = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto err_usage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_tmp_common(&fd, &s, type, NULL) < 0)
|
if (optind < argc)
|
||||||
err_no_cleanup(errno, "%s", s);
|
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 < 6)
|
||||||
|
err_no_cleanup(stfu, EINVAL,
|
||||||
|
"template must end in at least 6 X");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 defined(__OpenBSD__) && defined(OpenBSD)
|
||||||
#if (OpenBSD) >= 509
|
#if (OpenBSD) >= 509
|
||||||
if (pledge("stdio", NULL) == -1)
|
if (pledge("stdio", NULL) == -1)
|
||||||
err_no_cleanup(errno, "pledge, exit");
|
err_no_cleanup(stfu, errno, "pledge, exit");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
err_no_cleanup(EFAULT, "bad string initialisation");
|
err_no_cleanup(stfu, EFAULT, "bad string initialisation");
|
||||||
|
|
||||||
if (*s == '\0')
|
if (*s == '\0')
|
||||||
err_no_cleanup(EFAULT, "empty string initialisation");
|
err_no_cleanup(stfu, EFAULT, "empty string initialisation");
|
||||||
|
|
||||||
if (slen(s, maxlen, &len) < 0)
|
if (slen(s, maxlen, &len) < 0)
|
||||||
err_no_cleanup(EFAULT, "unterminated string initialisation");
|
err_no_cleanup(stfu, EFAULT, "unterminated string initialisiert");
|
||||||
|
|
||||||
printf("%s\n", s);
|
printf("%s\n", s);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
|
err_usage:
|
||||||
|
err_no_cleanup(stfu, EINVAL,
|
||||||
|
"usage: %s [-d] [-p dir] [template]\n", getnvmprogname());
|
||||||
}/*
|
}/*
|
||||||
|
|
||||||
|
|
||||||
@@ -140,3 +197,15 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,34 +36,34 @@ main(int argc, char *argv[])
|
|||||||
size_t c;
|
size_t c;
|
||||||
|
|
||||||
if (lbgetprogname(argv[0]) == NULL)
|
if (lbgetprogname(argv[0]) == NULL)
|
||||||
err_no_cleanup(errno, "could not set progname");
|
err_no_cleanup(0, errno, "could not set progname");
|
||||||
|
|
||||||
/* https://man.openbsd.org/pledge.2
|
/* https://man.openbsd.org/pledge.2
|
||||||
https://man.openbsd.org/unveil.2 */
|
https://man.openbsd.org/unveil.2 */
|
||||||
#if defined(__OpenBSD__) && defined(OpenBSD)
|
#if defined(__OpenBSD__) && defined(OpenBSD)
|
||||||
#if (OpenBSD) >= 604
|
#if (OpenBSD) >= 604
|
||||||
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
|
if (pledge("stdio flock rpath wpath cpath unveil", NULL) == -1)
|
||||||
err_no_cleanup(errno, "pledge plus unveil, main");
|
err_no_cleanup(0, errno, "pledge plus unveil, main");
|
||||||
if (unveil("/dev/null", "r") == -1)
|
if (unveil("/dev/null", "r") == -1)
|
||||||
err_no_cleanup(errno, "unveil r: /dev/null");
|
err_no_cleanup(0, errno, "unveil r: /dev/null");
|
||||||
#elif (OpenBSD) >= 509
|
#elif (OpenBSD) >= 509
|
||||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||||
err_no_cleanup(errno, "pledge, main");
|
err_no_cleanup(0, errno, "pledge, main");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef S_ISREG
|
#ifndef S_ISREG
|
||||||
err_no_cleanup(ECANCELED,
|
err_no_cleanup(0, ECANCELED,
|
||||||
"Can't determine file types (S_ISREG undefined)");
|
"Can't determine file types (S_ISREG undefined)");
|
||||||
#endif
|
#endif
|
||||||
#if ((CHAR_BIT) != 8)
|
#if ((CHAR_BIT) != 8)
|
||||||
err_no_cleanup(ECANCELED, "Unsupported char size");
|
err_no_cleanup(0, ECANCELED, "Unsupported char size");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
x = xstart(argc, argv);
|
x = xstart(argc, argv);
|
||||||
|
|
||||||
if (x == NULL)
|
if (x == NULL)
|
||||||
err_no_cleanup(ECANCELED, "NULL state on init");
|
err_no_cleanup(0, ECANCELED, "NULL state on init");
|
||||||
|
|
||||||
/* parse user command */
|
/* parse user command */
|
||||||
/* TODO: CHECK ACCESSES VIA xstatus() */
|
/* TODO: CHECK ACCESSES VIA xstatus() */
|
||||||
@@ -80,29 +80,29 @@ main(int argc, char *argv[])
|
|||||||
|
|
||||||
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
|
if ((us.cmd[i].flags & O_ACCMODE) == O_RDONLY) {
|
||||||
if (unveil(us.f.fname, "r") == -1)
|
if (unveil(us.f.fname, "r") == -1)
|
||||||
err(errno, "%s: unveil r", us.f.fname);
|
b0rk(errno, "%s: unveil r", us.f.fname);
|
||||||
} else {
|
} else {
|
||||||
if (unveil(us.f.fname, "rwc") == -1)
|
if (unveil(us.f.fname, "rwc") == -1)
|
||||||
err(errno, "%s: unveil rw", us.f.fname);
|
b0rk(errno, "%s: unveil rw", us.f.fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unveil(us.f.tname, "rwc") == -1)
|
if (unveil(us.f.tname, "rwc") == -1)
|
||||||
err(errno, "unveil rwc: %s", us.f.tname);
|
b0rk(errno, "unveil rwc: %s", us.f.tname);
|
||||||
|
|
||||||
if (unveil(NULL, NULL) == -1)
|
if (unveil(NULL, NULL) == -1)
|
||||||
err(errno, "unveil block (rw)");
|
b0rk(errno, "unveil block (rw)");
|
||||||
|
|
||||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||||
err(errno, "pledge (kill unveil)");
|
b0rk(errno, "pledge (kill unveil)");
|
||||||
|
|
||||||
#elif (OpenBSD) >= 509
|
#elif (OpenBSD) >= 509
|
||||||
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
if (pledge("stdio flock rpath wpath cpath", NULL) == -1)
|
||||||
err(errno, "pledge");
|
b0rk(errno, "pledge");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (cmd->run == NULL)
|
if (cmd->run == NULL)
|
||||||
err(errno, "Command not set");
|
b0rk(errno, "Command not set");
|
||||||
|
|
||||||
|
|
||||||
sanitize_command_list();
|
sanitize_command_list();
|
||||||
@@ -121,10 +121,10 @@ main(int argc, char *argv[])
|
|||||||
write_to_gbe_bin();
|
write_to_gbe_bin();
|
||||||
|
|
||||||
if (exit_cleanup() == -1)
|
if (exit_cleanup() == -1)
|
||||||
err(EIO, "%s: close", f->fname);
|
b0rk(EIO, "%s: close", f->fname);
|
||||||
|
|
||||||
if (f->io_err_gbe_bin)
|
if (f->io_err_gbe_bin)
|
||||||
err(EIO, "%s: error writing final file");
|
b0rk(EIO, "%s: error writing final file");
|
||||||
|
|
||||||
free_if_null(&f->tname);
|
free_if_null(&f->tname);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user