diff options
author | Philip Guenther <guenther@cvs.openbsd.org> | 2015-02-11 23:14:47 +0000 |
---|---|---|
committer | Philip Guenther <guenther@cvs.openbsd.org> | 2015-02-11 23:14:47 +0000 |
commit | 74e19fb222f0da41bf9c3e7aaed9fb36e43e9dba (patch) | |
tree | 054ba50c6c2f7808e7e66a27f198804585c7bfee /bin/pax/file_subs.c | |
parent | cdb3a208e6fc7860ce3497d03d3b26434e9d9653 (diff) |
Take II, this time without an incorrect mode test.
For directories whose times or mode will be fixed up in the clean-up pass,
record their dev+ino and then use open(O_DIRECTORY)+fstat() to verify that
we're updating the correct directory before using futimens() and fchmod().
ok sthen@ millert@
Diffstat (limited to 'bin/pax/file_subs.c')
-rw-r--r-- | bin/pax/file_subs.c | 117 |
1 files changed, 85 insertions, 32 deletions
diff --git a/bin/pax/file_subs.c b/bin/pax/file_subs.c index f4054b53b89..ca546c080a6 100644 --- a/bin/pax/file_subs.c +++ b/bin/pax/file_subs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: file_subs.c,v 1.40 2015/02/05 22:32:20 sthen Exp $ */ +/* $OpenBSD: file_subs.c,v 1.41 2015/02/11 23:14:46 guenther Exp $ */ /* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */ /*- @@ -56,10 +56,6 @@ mk_link(char *, struct stat *, char *, int); * and setting access modes, uid/gid and times of files */ -#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) -#define SETBITS (S_ISUID | S_ISGID) -#define ABITS (FILEBITS | SETBITS) - /* * file_creat() * Create and open a file. @@ -469,33 +465,36 @@ badlink: * rights. This allows nodes in the archive that are children * of this directory to be extracted without failure. Both time * and modes will be fixed after the entire archive is read and - * before pax exits. + * before pax exits. To do that safely, we want the dev+ino + * of the directory we created. */ - if (access(nm, R_OK | W_OK | X_OK) < 0) { - if (lstat(nm, &sb) < 0) { - syswarn(0, errno,"Could not access %s (stat)", - arcn->name); - set_pmode(nm,file_mode | S_IRWXU); - } else { - /* - * We have to add rights to the dir, so we make - * sure to restore the mode. The mode must be - * restored AS CREATED and not as stored if - * pmode is not set. - */ - set_pmode(nm, - ((sb.st_mode & FILEBITS) | S_IRWXU)); - if (!pmode) - arcn->sb.st_mode = sb.st_mode; - } + if (lstat(nm, &sb) < 0) { + syswarn(0, errno,"Could not access %s (stat)", nm); + } else if (access(nm, R_OK | W_OK | X_OK) < 0) { + /* + * We have to add rights to the dir, so we make + * sure to restore the mode. The mode must be + * restored AS CREATED and not as stored if + * pmode is not set. + */ + set_pmode(nm, + ((sb.st_mode & FILEBITS) | S_IRWXU)); + if (!pmode) + arcn->sb.st_mode = sb.st_mode; /* - * we have to force the mode to what was set here, - * since we changed it from the default as created. + * we have to force the mode to what was set + * here, since we changed it from the default + * as created. */ + arcn->sb.st_dev = sb.st_dev; + arcn->sb.st_ino = sb.st_ino; add_dir(nm, &(arcn->sb), 1); - } else if (pmode || patime || pmtime) + } else if (pmode || patime || pmtime) { + arcn->sb.st_dev = sb.st_dev; + arcn->sb.st_ino = sb.st_ino; add_dir(nm, &(arcn->sb), 0); + } } if (patime || pmtime) @@ -766,6 +765,60 @@ fset_pmode(char *fnm, int fd, mode_t mode) } /* + * set_attr() + * Given a DIRDATA, restore the mode and times as indicated, but + * only after verifying that it's the directory that we wanted. + */ +int +set_attr(const struct file_times *ft, int force_times, mode_t mode, + int do_mode, int in_sig) +{ + struct stat sb; + int fd, r; + + if (!do_mode && !force_times && !patime && !pmtime) + return (0); + + /* + * We could legitimately go through a symlink here, + * so do *not* use O_NOFOLLOW. The dev+ino check will + * protect us from evil. + */ + fd = open(ft->ft_name, O_RDONLY | O_DIRECTORY); + if (fd == -1) { + if (!in_sig) + syswarn(1, errno, "Unable to restore mode and times" + " for directory: %s", ft->ft_name); + return (-1); + } + + if (fstat(fd, &sb) == -1) { + if (!in_sig) + syswarn(1, errno, "Unable to stat directory: %s", + ft->ft_name); + r = -1; + } else if (ft->ft_ino != sb.st_ino || ft->ft_dev != sb.st_dev) { + if (!in_sig) + paxwarn(1, "Directory vanished before restoring" + " mode and times: %s", ft->ft_name); + r = -1; + } else { + /* Whew, it's a match! Is there anything to change? */ + if (do_mode && (mode & ABITS) != (sb.st_mode & ABITS)) + fset_pmode(ft->ft_name, fd, mode); + if (((force_times || patime) && ft->ft_atime != sb.st_atime) || + ((force_times || pmtime) && ft->ft_mtime != sb.st_mtime)) + fset_ftime(ft->ft_name, fd, ft->ft_mtime, + ft->ft_atime, force_times); + r = 0; + } + close(fd); + + return (r); +} + + +/* * file_write() * Write/copy a file (during copy or archive extract). This routine knows * how to copy files with lseek holes in it. (Which are read as file @@ -957,15 +1010,15 @@ rdfile_close(ARCHD *arcn, int *fd) if (*fd < 0) return; - (void)close(*fd); - *fd = -1; - if (!tflag) - return; - /* * user wants last access time reset */ - set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1); + if (tflag) + fset_ftime(arcn->org_name, *fd, arcn->sb.st_mtime, + arcn->sb.st_atime, 1); + + (void)close(*fd); + *fd = -1; } /* |