aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--NEWS6
-rw-r--r--src/copyin.c34
-rw-r--r--src/extern.h4
-rw-r--r--src/util.c79
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/setstat01.at42
-rw-r--r--tests/setstat02.at44
-rw-r--r--tests/testsuite.at3
8 files changed, 199 insertions, 15 deletions
diff --git a/NEWS b/NEWS
index 87c7111..03c8869 100644
--- a/NEWS
+++ b/NEWS
@@ -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);
diff --git a/src/util.c b/src/util.c
index 2b765d9..1eb92ae 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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])

Return to:

Send suggestions and report system problems to the System administrator.