From 400d6b61b98c3be5eebb095019dfe6310e1ca253 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Fri, 31 Jul 2009 17:45:38 +0300 Subject: 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. --- src/copyin.c | 34 +++++++++++++++++++++++--- src/extern.h | 4 +-- src/util.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 102 insertions(+), 15 deletions(-) (limited to 'src') 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 -- cgit v1.2.1