diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-07-31 17:45:38 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-07-31 17:45:38 +0300 |
commit | 400d6b61b98c3be5eebb095019dfe6310e1ca253 (patch) | |
tree | 27c542ecd4643c27d55f31aca0ef222779a02527 | |
parent | 15d29bd0863a819ed950f65523b6000961d1213e (diff) | |
download | cpio-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.
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | src/copyin.c | 34 | ||||
-rw-r--r-- | src/extern.h | 4 | ||||
-rw-r--r-- | src/util.c | 79 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/setstat01.at | 42 | ||||
-rw-r--r-- | tests/setstat02.at | 44 | ||||
-rw-r--r-- | tests/testsuite.at | 3 |
8 files changed, 199 insertions, 15 deletions
@@ -4,12 +4,18 @@ See the end of file for copying conditions. Please send cpio bug reports to <bug-cpio@gnu.org>. Version 2.10.90 (Git) * Fix mt build. +* In copy-in mode, if directory attributes do not permit writing to it, +setting them is delayed until the end of run. This allows to correctly +extract files in such directories. +* In copy-in mode, permissions of a directory are restored if it +appears in the file list after files in it (find . -depth). This fixes +debian bug #458079. Version 2.10 - Sergey Poznyakoff, 2009-06-20 * Ensure record headers are properly packed (fix builds on ARM). * Fix exit codes to reliably indicate success or failure of the operation. diff --git a/src/copyin.c b/src/copyin.c index 59483da..5b7594b 100644 --- a/src/copyin.c +++ b/src/copyin.c @@ -563,21 +563,46 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des) for this file, create any other links to it which we defered. */ create_defered_links (file_hdr); } } +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) { int res; /* Result of various function calls. */ #ifdef HPUX_CDF 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; /* Strip any trailing `/'s off the filename; tar puts them on. We might as well do it here in case anybody else does too, since they cause strange things to happen. */ @@ -607,20 +632,20 @@ copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir) (file_hdr->c_name [cdf_char] == '+') ) { file_hdr->c_name [cdf_char] = '\0'; 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) { /* In some odd cases where the file_hdr->c_name includes `.', the directory may have actually been created by create_all_directories(), so the mkdir will fail @@ -642,13 +667,14 @@ copyin_directory (struct cpio_file_stat *file_hdr, int existing_dir) error (0, 0, _("%s is not a directory"), quotearg_colon (file_hdr->c_name)); return; } } - set_perms (-1, file_hdr); + if (!setstat_delayed && repair_delayed_set_stat (file_hdr) == 0) + set_perms (-1, file_hdr); } static void copyin_device (struct cpio_file_stat* file_hdr) { int res; /* Result of various function calls. */ diff --git a/src/extern.h b/src/extern.h index a03508b..a832897 100644 --- a/src/extern.h +++ b/src/extern.h @@ -193,12 +193,13 @@ void write_nuls_to_file (off_t num_bytes, int out_des, #endif /* SYMLINK_USES_UMASK */ 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); /* FIXME: These two defines should be defined in paxutils */ #define LG_8 3 #define LG_16 4 @@ -207,10 +208,9 @@ uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase); #define FROM_OCTAL(f) from_ascii (f, sizeof f, LG_8) #define FROM_HEX(f) from_ascii (f, sizeof f, LG_16) 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); @@ -1253,23 +1253,66 @@ stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st) hdr->c_mode |= CP_IFSOCK; #endif #ifdef S_ISNWK 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; hdr->c_filesize = st->st_size; hdr->c_chksum = 0; 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 #ifndef HAVE_FCHMOD # define HAVE_FCHMOD 0 #endif @@ -1286,13 +1329,13 @@ fchown_or_chown (int fd, const char *name, uid_t uid, uid_t gid) int 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 set_perms (int fd, struct cpio_file_stat *header) { if (!no_chown_flag) @@ -1391,39 +1434,57 @@ delay_set_stat (char const *file_name, struct stat *st, } /* Update the delayed_set_stat info for an intermediate directory 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) { struct stat st; 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 && st.st_ino == dir_stat_info->st_ino) { stat_to_cpio (&data->stat, dir_stat_info); 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 apply_delayed_set_stat () { while (delayed_set_stat_head) diff --git a/tests/Makefile.am b/tests/Makefile.am index b4ad2ba..9e3b97b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -42,12 +42,14 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac ## ------------ ## TESTSUITE_AT = \ testsuite.at\ inout.at\ interdir.at\ + setstat01.at\ + setstat02.at\ symlink.at\ version.at TESTSUITE = $(srcdir)/testsuite AUTOTEST = $(AUTOM4TE) --language=autotest diff --git a/tests/setstat01.at b/tests/setstat01.at new file mode 100644 index 0000000..7c7b51a --- /dev/null +++ b/tests/setstat01.at @@ -0,0 +1,42 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Copyright (C) 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +AT_SETUP([delayed setstat]) +AT_KEYWORDS([setstat setstat01]) + +# In cpio up to 2.10, in copy-in mode, permissions and ownership of +# created directories were set right after creating them. If directory +# permissions did not allow writing to it, cpio was unable to populate +# the directory. + +AT_CHECK([ +mkdir dir +echo "test file" > file +chmod 500 dir + +find dir | cpio -o --quiet > archive +mv dir old + +cpio -i --quiet < archive +genfile --stat=mode.777 dir +], +[0], +[500 +]) + +AT_CLEANUP + + diff --git a/tests/setstat02.at b/tests/setstat02.at new file mode 100644 index 0000000..0b919c6 --- /dev/null +++ b/tests/setstat02.at @@ -0,0 +1,44 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- +# Copyright (C) 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +AT_SETUP([delayed setstat (with -depth)]) +AT_KEYWORDS([setstat setstat01]) + +# Cpio versions up to 2.10 failed to restore directory permissions, if +# the directory was already present on dist (e.g. when using find . -depth). +# +# References: +# <20090620185721.GA18708@scru.org> +# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=458079 + +AT_CHECK([ +mkdir dir +echo "test file" > file +chmod 500 dir + +find dir -depth | cpio -o --quiet > archive +mv dir old + +cpio -i --quiet < archive +genfile --stat=mode.777 dir +], +[0], +[500 +]) + +AT_CLEANUP + + diff --git a/tests/testsuite.at b/tests/testsuite.at index eea6c3f..16d64dd 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -29,6 +29,9 @@ AT_TESTED([cpio]) m4_include([version.at]) m4_include([inout.at]) m4_include([symlink.at]) m4_include([interdir.at]) + +m4_include([setstat01.at]) +m4_include([setstat02.at]) |