diff options
-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
@@ -7,6 +7,12 @@ 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 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); @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index b4ad2ba..9e3b97b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -45,6 +45,8 @@ TESTSUITE_AT = \ testsuite.at\ inout.at\ interdir.at\ + setstat01.at\ + setstat02.at\ symlink.at\ version.at 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 @@ -32,3 +32,6 @@ m4_include([version.at]) m4_include([inout.at]) m4_include([symlink.at]) m4_include([interdir.at]) + +m4_include([setstat01.at]) +m4_include([setstat02.at]) |