aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-07-31 17:45:38 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-07-31 17:45:38 +0300
commit400d6b61b98c3be5eebb095019dfe6310e1ca253 (patch)
tree27c542ecd4643c27d55f31aca0ef222779a02527 /src
parent15d29bd0863a819ed950f65523b6000961d1213e (diff)
downloadcpio-400d6b61b98c3be5eebb095019dfe6310e1ca253.tar.gz
cpio-400d6b61b98c3be5eebb095019dfe6310e1ca253.tar.bz2
Delay setting directory attributes until end of run, if they do not permit writing. Fix debian bug #458079.
* src/copyin.c (copyin_mkdir): New function. (copyin_directory): Use copyin_mkdir to create directory. Call set_perms only when safe, otherwise use repair_delayed_set_stat. * src/extern.h (cpio_to_stat): New prototype. (repair_delayed_set_stat): Change prototype. * src/util.c (cpio_to_stat): New function. (repair_delayed_set_stat): New function. * tests/setstat01.at: New test case. * tests/setstat02.at: New test case. * tests/Makefile.am (TESTSUITE_AT): Add setstat01.at and setstat02.at * tests/testsuite.at: Include setstat01.at and setstat02.at. * NEWS: Update.
Diffstat (limited to 'src')
-rw-r--r--src/copyin.c34
-rw-r--r--src/extern.h4
-rw-r--r--src/util.c79
3 files changed, 102 insertions, 15 deletions
diff --git a/src/copyin.c b/src/copyin.c
index 59483da..5b7594b 100644
--- a/src/copyin.c
+++ b/src/copyin.c
@@ -566,6 +566,30 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des)
}
}
+static int
+copyin_mkdir (struct cpio_file_stat *file_hdr, int *setstat_delayed)
+{
+ int rc;
+ mode_t mode = file_hdr->c_mode;
+ if (!(file_hdr->c_mode & S_IWUSR))
+ {
+ rc = mkdir (file_hdr->c_name, mode | S_IWUSR);
+ if (rc == 0)
+ {
+ struct stat st;
+ cpio_to_stat (&st, file_hdr);
+ delay_set_stat (file_hdr->c_name, &st, false);
+ *setstat_delayed = 1;
+ }
+ }
+ else
+ {
+ rc = mkdir (file_hdr->c_name, mode);
+ *setstat_delayed = 0;
+ }
+ return rc;
+}
+
static void
copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir)
{
@@ -574,7 +598,8 @@ copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir)
int cdf_flag; /* True if file is a CDF. */
int cdf_char; /* Index of `+' char indicating a CDF. */
#endif
-
+ int setstat_delayed = 0;
+
if (to_stdout_option)
return;
@@ -610,14 +635,14 @@ copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir)
cdf_flag = 1;
}
#endif
- res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ res = copyin_mkdir (file_hdr, &setstat_delayed);
}
else
res = 0;
if (res < 0 && create_dir_flag)
{
create_all_directories (file_hdr->c_name);
- res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ res = copyin_mkdir (file_hdr, &setstat_delayed);
}
if (res < 0)
{
@@ -645,7 +670,8 @@ copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir)
}
}
- set_perms (-1, file_hdr);
+ if (!setstat_delayed && repair_delayed_set_stat (file_hdr) == 0)
+ set_perms (-1, file_hdr);
}
static void
diff --git a/src/extern.h b/src/extern.h
index a03508b..a832897 100644
--- a/src/extern.h
+++ b/src/extern.h
@@ -196,6 +196,7 @@ void set_perms (int fd, struct cpio_file_stat *header);
void set_file_times (int fd, const char *name, unsigned long atime,
unsigned long mtime);
void stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st);
+void cpio_to_stat (struct stat *st, struct cpio_file_stat *hdr);
void cpio_safer_name_suffix (char *name, bool link_target,
bool absolute_names, bool strip_leading_dots);
@@ -210,7 +211,6 @@ uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase);
void delay_set_stat (char const *file_name, struct stat *st,
mode_t invert_permissions);
-void repair_delayed_set_stat (char const *dir,
- struct stat *dir_stat_info);
+int repair_delayed_set_stat (struct cpio_file_stat *file_hdr);
void apply_delayed_set_stat (void);
diff --git a/src/util.c b/src/util.c
index 2b765d9..1eb92ae 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1256,9 +1256,9 @@ stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st)
else if (S_ISNWK (st->st_mode))
hdr->c_mode |= CP_IFNWK;
#endif
+ hdr->c_nlink = st->st_nlink;
hdr->c_uid = CPIO_UID (st->st_uid);
hdr->c_gid = CPIO_GID (st->st_gid);
- hdr->c_nlink = st->st_nlink;
hdr->c_rdev_maj = major (st->st_rdev);
hdr->c_rdev_min = minor (st->st_rdev);
hdr->c_mtime = st->st_mtime;
@@ -1267,6 +1267,49 @@ stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st)
hdr->c_tar_linkname = NULL;
}
+void
+cpio_to_stat (struct stat *st, struct cpio_file_stat *hdr)
+{
+ memset (st, 0, sizeof (*st));
+ st->st_dev = makedev (hdr->c_dev_maj, hdr->c_dev_min);
+ st->st_ino = hdr->c_ino;
+ st->st_mode = hdr->c_mode & 0777;
+ if (hdr->c_mode & CP_IFREG)
+ st->st_mode |= S_IFREG;
+ else if (hdr->c_mode & CP_IFDIR)
+ st->st_mode |= S_IFDIR;
+#ifdef S_IFBLK
+ else if (hdr->c_mode & CP_IFBLK)
+ st->st_mode |= S_IFBLK;
+#endif
+#ifdef S_IFCHR
+ else if (hdr->c_mode & CP_IFCHR)
+ st->st_mode |= S_IFCHR;
+#endif
+#ifdef S_IFFIFO
+ else if (hdr->c_mode & CP_IFIFO)
+ st->st_mode |= S_IFIFO;
+#endif
+#ifdef S_IFLNK
+ else if (hdr->c_mode & CP_IFLNK)
+ st->st_mode |= S_IFLNK;
+#endif
+#ifdef S_IFSOCK
+ else if (hdr->c_mode & CP_IFSOCK)
+ st->st_mode |= S_IFSOCK;
+#endif
+#ifdef S_IFNWK
+ else if (hdr->c_mode & CP_IFNWK)
+ st->st_mode |= S_IFNWK;
+#endif
+ st->st_nlink = hdr->c_nlink;
+ st->st_uid = CPIO_UID (hdr->c_uid);
+ st->st_gid = CPIO_GID (hdr->c_gid);
+ st->st_rdev = makedev (hdr->c_rdev_maj, hdr->c_rdev_min);
+ st->st_mtime = hdr->c_mtime;
+ st->st_size = hdr->c_filesize;
+}
+
#ifndef HAVE_FCHOWN
# define HAVE_FCHOWN 0
#endif
@@ -1289,7 +1332,7 @@ fchmod_or_chmod (int fd, const char *name, mode_t mode)
if (HAVE_FCHMOD && fd != -1)
return fchmod (fd, mode);
else
- return chmod(name, mode);
+ return chmod (name, mode);
}
void
@@ -1394,9 +1437,8 @@ delay_set_stat (char const *file_name, struct stat *st,
created within the file name of DIR. The intermediate directory turned
out to be the same as this directory, e.g. due to ".." or symbolic
links. *DIR_STAT_INFO is the status of the directory. */
-void
-repair_delayed_set_stat (char const *dir,
- struct stat *dir_stat_info)
+int
+repair_inter_delayed_set_stat (struct stat *dir_stat_info)
{
struct delayed_set_stat *data;
for (data = delayed_set_stat_head; data; data = data->next)
@@ -1405,7 +1447,7 @@ repair_delayed_set_stat (char const *dir,
if (stat (data->stat.c_name, &st) != 0)
{
stat_error (data->stat.c_name);
- return;
+ return -1;
}
if (st.st_dev == dir_stat_info->st_dev
@@ -1415,12 +1457,31 @@ repair_delayed_set_stat (char const *dir,
data->invert_permissions =
((dir_stat_info->st_mode ^ st.st_mode)
& MODE_RWX & ~ newdir_umask);
- return;
+ return 0;
}
}
+ return 1;
+}
+
+/* Update the delayed_set_stat info for a directory matching
+ FILE_HDR.
- ERROR ((0, 0, _("%s: Unexpected inconsistency when making directory"),
- quotearg_colon (dir)));
+ Return 0 if such info was found, 1 otherwise. */
+int
+repair_delayed_set_stat (struct cpio_file_stat *file_hdr)
+{
+ struct delayed_set_stat *data;
+ for (data = delayed_set_stat_head; data; data = data->next)
+ {
+ if (strcmp (file_hdr->c_name, data->stat.c_name) == 0)
+ {
+ data->invert_permissions = 0;
+ memcpy (&data->stat, file_hdr,
+ offsetof (struct cpio_file_stat, c_name));
+ return 0;
+ }
+ }
+ return 1;
}
void

Return to:

Send suggestions and report system problems to the System administrator.