summaryrefslogtreecommitdiff
path: root/archivers/pax/files/file_subs.c
diff options
context:
space:
mode:
Diffstat (limited to 'archivers/pax/files/file_subs.c')
-rw-r--r--archivers/pax/files/file_subs.c123
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);