diff options
Diffstat (limited to 'archivers/pax/files/file_subs.c')
-rw-r--r-- | archivers/pax/files/file_subs.c | 123 |
1 files changed, 80 insertions, 43 deletions
diff --git a/archivers/pax/files/file_subs.c b/archivers/pax/files/file_subs.c index e5012a588a3..f321ae76ae4 100644 --- a/archivers/pax/files/file_subs.c +++ b/archivers/pax/files/file_subs.c @@ -1,4 +1,4 @@ -/* $NetBSD: file_subs.c,v 1.4 2003/12/20 04:45:04 grant Exp $ */ +/* $NetBSD: file_subs.c,v 1.5 2004/06/20 10:11:02 grant Exp $ */ /*- * Copyright (c) 1992 Keith Muller. @@ -44,7 +44,7 @@ #if 0 static char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93"; #else -__RCSID("$NetBSD: file_subs.c,v 1.4 2003/12/20 04:45:04 grant Exp $"); +__RCSID("$NetBSD: file_subs.c,v 1.5 2004/06/20 10:11:02 grant Exp $"); #endif #endif /* not lint */ @@ -88,17 +88,27 @@ __RCSID("$NetBSD: file_subs.c,v 1.4 2003/12/20 04:45:04 grant Exp $"); #include "extern.h" #include "options.h" +char *xtmp_name; + static int mk_link(char *,struct stat *,char *, int); +static int warn_broken; + /* * routines that deal with file operations such as: creating, removing; * and setting access modes, uid/gid and times of files */ +#define SET_BITS (S_ISUID | S_ISGID) +#define FILE_BITS (S_IRWXU | S_IRWXG | S_IRWXO) +#define A_BITS (FILE_BITS | SET_BITS | S_ISVTX) -#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) -#define SETBITS (S_ISUID | S_ISGID) -#define ABITS (FILEBITS | SETBITS) +/* + * The S_ISVTX (sticky bit) can be set by non-superuser on directories + * but not other kinds of files. + */ +#define FILEBITS(dir) ((dir) ? (FILE_BITS | S_ISVTX) : FILE_BITS) +#define SETBITS(dir) ((dir) ? SET_BITS : (SET_BITS | S_ISVTX)) /* * file_creat() @@ -111,46 +121,55 @@ int file_creat(ARCHD *arcn) { int fd = -1; - mode_t file_mode; int oerrno; /* - * assume file doesn't exist, so just try to create it, most times this - * works. We have to take special handling when the file does exist. To - * detect this, we use O_EXCL. For example when trying to create a - * file and a character device or fifo exists with the same name, we - * can accidently open the device by mistake (or block waiting to open) - * If we find that the open has failed, then spend the effort to - * figure out why. This strategy was found to have better average - * performance in common use than checking the file (and the path) - * first with lstat. + * Some horribly busted tar implementations, have directory nodes + * that end in a /, but they mark as files. Compensate for that + * by not creating a directory node at this point, but a file node, + * and not creating the temp file. */ - file_mode = arcn->sb.st_mode & FILEBITS; - if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, - file_mode)) >= 0) - return(fd); - + if (arcn->nlen != 0 && arcn->name[arcn->nlen - 1] == '/') { + if (!warn_broken) { + tty_warn(0, "Archive was created with a broken tar;" + " file `%s' is a directory, but marked as plain.", + arcn->name); + warn_broken = 1; + } + return -1; + } /* - * the file seems to exist. First we try to get rid of it (found to be - * the second most common failure when traced). If this fails, only - * then we go to the expense to check and create the path to the file + * Create a temporary file name so that the file doesn't have partial + * contents while restoring. */ - if (unlnk_exist(arcn->name, arcn->type) != 0) + arcn->tmp_name = malloc(arcn->nlen + 8); + if (arcn->tmp_name == NULL) { + syswarn(1, errno, "Cannot malloc %d bytes", arcn->nlen + 8); return(-1); + } + if (xtmp_name) + abort(); + xtmp_name = arcn->tmp_name; for (;;) { /* - * try to open it again, if this fails, check all the nodes in - * the path and give it a final try. if chk_path() finds that - * it cannot fix anything, we will skip the last attempt + * try to create the temporary file we use to restore the + * contents info. if this fails, keep checking all the nodes + * in the path until chk_path() finds that it cannot fix + * anything further. if that happens we just give up. */ - if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC, - file_mode)) >= 0) + (void)snprintf(arcn->tmp_name, arcn->nlen + 8, "%s.XXXXXX", + arcn->name); + fd = mkstemp(arcn->tmp_name); + if (fd >= 0) break; oerrno = errno; if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { (void)fflush(listf); - syswarn(1, oerrno, "Cannot create %s", arcn->name); + syswarn(1, oerrno, "Cannot create %s", arcn->tmp_name); + xtmp_name = NULL; + free(arcn->tmp_name); + arcn->tmp_name = NULL; return(-1); } } @@ -174,7 +193,7 @@ file_close(ARCHD *arcn, int fd) return; if (close(fd) < 0) syswarn(0, errno, "Cannot close file descriptor on %s", - arcn->name); + arcn->tmp_name); /* * set owner/groups first as this may strip off mode bits we want @@ -182,23 +201,39 @@ file_close(ARCHD *arcn, int fd) * modification times. */ if (pids) - res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid); + res = set_ids(arcn->tmp_name, arcn->sb.st_uid, arcn->sb.st_gid); /* * IMPORTANT SECURITY NOTE: * if not preserving mode or we cannot set uid/gid, then PROHIBIT - * set uid/gid bits + * set uid/gid bits but restore the file modes (since mkstemp doesn't). */ if (!pmode || res) - arcn->sb.st_mode &= ~(SETBITS); + arcn->sb.st_mode &= ~SETBITS(0); if (pmode) - set_pmode(arcn->name, arcn->sb.st_mode); + set_pmode(arcn->tmp_name, arcn->sb.st_mode); + else + set_pmode(arcn->tmp_name, arcn->sb.st_mode & FILEBITS(0)); if (patime || pmtime) - set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); + set_ftime(arcn->tmp_name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); + /* + * Finally, now the temp file is fully instantiated rename it to + * the desired file name. + */ + if (rename(arcn->tmp_name, arcn->name) < 0) { + syswarn(0, errno, "Cannot rename %s to %s", + arcn->tmp_name, arcn->name); + (void)unlink(arcn->tmp_name); + } + #if HAVE_STRUCT_STAT_ST_FLAGS if (pfflags && arcn->type != PAX_SLK) set_chflags(arcn->name, arcn->sb.st_flags); #endif + + free(arcn->tmp_name); + arcn->tmp_name = NULL; + xtmp_name = NULL; } /* @@ -395,7 +430,7 @@ node_creat(ARCHD *arcn) * file and link creation routines, this method seems to exhibit the * best performance in general use workloads. */ - file_mode = arcn->sb.st_mode & FILEBITS; + file_mode = arcn->sb.st_mode & FILEBITS(arcn->type == PAX_DIR); for (;;) { switch(arcn->type) { @@ -500,7 +535,7 @@ badlink: * set uid/gid bits */ if (!pmode || res) - arcn->sb.st_mode &= ~(SETBITS); + arcn->sb.st_mode &= ~SETBITS(arcn->type == PAX_DIR); if (pmode) set_pmode(arcn->name, arcn->sb.st_mode); @@ -526,8 +561,9 @@ badlink: * restored AS CREATED and not as stored if * pmode is not set. */ - set_pmode(nm, - ((sb.st_mode & FILEBITS) | S_IRWXU)); + set_pmode(nm, ((sb.st_mode & + FILEBITS(arcn->type == PAX_DIR)) | + S_IRWXU)); if (!pmode) arcn->sb.st_mode = sb.st_mode; } @@ -620,7 +656,7 @@ unlnk_exist(char *name, int type) */ int -chk_path( char *name, uid_t st_uid, gid_t st_gid) +chk_path(char *name, uid_t st_uid, gid_t st_gid) { char *spt = name; struct stat sb; @@ -683,7 +719,8 @@ chk_path( char *name, uid_t st_uid, gid_t st_gid) */ if ((access(name, R_OK | W_OK | X_OK) < 0) && (lstat(name, &sb) == 0)) { - set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU)); + set_pmode(name, ((sb.st_mode & FILEBITS(0)) | + S_IRWXU)); add_dir(name, spt - name, &sb, 1); } *(spt++) = '/'; @@ -776,7 +813,7 @@ set_ids(char *fnm, uid_t uid, gid_t gid) void set_pmode(char *fnm, mode_t mode) { - mode &= ABITS; + mode &= A_BITS; if (lchmod(fnm, mode)) { (void)fflush(listf); syswarn(1, errno, "Cannot set permissions on %s", fnm); |