summaryrefslogtreecommitdiffstats
path: root/lib/libarchive/archive_read_extract.c
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2004-04-26 23:37:54 +0000
committerkientzle <kientzle@FreeBSD.org>2004-04-26 23:37:54 +0000
commit736725bcabf6c72ac5bfda301f3aa3b447ac4c6b (patch)
treef0f5a81c35118c56a6a3ac1a5d72fd50e2cdf842 /lib/libarchive/archive_read_extract.c
parentf5f7c88a25b2bbf11fbacb20c1194ad48018294b (diff)
downloadFreeBSD-src-736725bcabf6c72ac5bfda301f3aa3b447ac4c6b.zip
FreeBSD-src-736725bcabf6c72ac5bfda301f3aa3b447ac4c6b.tar.gz
Update file flag handling.
The new fflags support in archive_entry supports Linux and FreeBSD file flags and is a bit more gracious about unrecognized flag names than strtofflags(3). This involves some minor API breakage. The default tar format ("restricted pax") now enables pax extensions when archiving files that have flags. In particular, copying dir heirarchies with 'bsdtar cf - -C src . | bsdtar xpf - -C dest' now preserves file flags. (Note the "p" on extract!) While I'm here, fill in some additional explanation in the archive_entry.3 manpage, fill in some missing MLINKS, mark some overlooked internal functions 'static', and make a few minor style fixes.
Diffstat (limited to 'lib/libarchive/archive_read_extract.c')
-rw-r--r--lib/libarchive/archive_read_extract.c79
1 files changed, 63 insertions, 16 deletions
diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c
index 6155c52..77d6bb0 100644
--- a/lib/libarchive/archive_read_extract.c
+++ b/lib/libarchive/archive_read_extract.c
@@ -44,6 +44,10 @@ __FBSDID("$FreeBSD$");
#include <string.h>
#include <tar.h>
#include <unistd.h>
+#ifdef LINUX
+#include <ext2fs/ext2_fs.h>
+#include <sys/ioctl.h>
+#endif
#include "archive.h"
#include "archive_string.h"
@@ -440,7 +444,11 @@ archive_read_extract_dir_create(struct archive *a, const char *name, int mode,
return (ARCHIVE_OK);
/* Unlink failed. It's okay if it failed because it's already a dir. */
- if (errno != EPERM) {
+ /*
+ * BSD returns EPERM for unlink on an dir,
+ * Linux returns EISDIR
+ */
+ if (errno != EPERM && errno != EISDIR) {
archive_set_error(a, errno, "Couldn't create dir");
return (ARCHIVE_WARN);
}
@@ -782,29 +790,31 @@ set_extended_perm(struct archive *a, struct archive_entry *entry, int flags)
static int
set_fflags(struct archive *a, struct archive_entry *entry)
{
- char *fflags;
- const char *fflagsc;
- char *fflags_p;
const char *name;
int ret;
unsigned long set, clear;
struct stat st;
+#ifdef LINUX
+ struct stat *stp;
+ int fd;
+ int err;
+ unsigned long newflags, oldflags;
+#endif
name = archive_entry_pathname(entry);
-
ret = ARCHIVE_OK;
- fflagsc = archive_entry_fflags(entry);
- if (fflagsc == NULL)
- return (ARCHIVE_OK);
-
- fflags = strdup(fflagsc);
- if (fflags == NULL)
- return (ARCHIVE_WARN);
+ archive_entry_fflags(entry, &set, &clear);
+ if (set == 0 && clear == 0)
+ return (ret);
#ifdef HAVE_CHFLAGS
- fflags_p = fflags;
- if (strtofflags(&fflags_p, &set, &clear) == 0 &&
- stat(name, &st) == 0) {
+ /*
+ * XXX Is the stat here really necessary? Or can I just use
+ * the 'set' flags directly? In particular, I'm not sure
+ * about the correct approach if we're overwriting an existing
+ * file that already has flags on it. XXX
+ */
+ if (stat(name, &st) == 0) {
st.st_flags &= ~clear;
st.st_flags |= set;
if (chflags(name, st.st_flags) != 0) {
@@ -814,8 +824,45 @@ set_fflags(struct archive *a, struct archive_entry *entry)
}
}
#endif
+ /* Linux has flags too, but no chflags syscall */
+#ifdef LINUX
+ /*
+ * Linux has no define for the flags that are only settable
+ * by the root user...
+ */
+#define SF_MASK (EXT2_IMMUTABLE_FL|EXT2_APPEND_FL)
+
+ /*
+ * XXX As above, this would be way simpler if we didn't have
+ * to read the current flags from disk. XXX
+ */
+ stp = archive_entry_stat(entry);
+ if ((S_ISREG(stp->st_mode) || S_ISDIR(stp->st_mode)) &&
+ ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0)) {
+ err = 1;
+ if (fd >= 0 && (ioctl(fd, EXT2_IOC_GETFLAGS, &oldflags) >= 0)) {
+ newflags = (oldflags & ~clear) | set;
+ if (ioctl(fd, EXT2_IOC_SETFLAGS, &newflags) >= 0) {
+ err = 0;
+ } else if (errno == EPERM) {
+ if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) {
+ newflags &= ~SF_MASK;
+ oldflags &= SF_MASK;
+ newflags |= oldflags;
+ if (ioctl(fd, EXT2_IOC_SETFLAGS, &newflags) >= 0)
+ err = 0;
+ }
+ }
+ }
+ close(fd);
+ if (err) {
+ archive_set_error(a, errno,
+ "Failed to set file flags");
+ ret = ARCHIVE_WARN;
+ }
+ }
+#endif
- free(fflags);
return (ret);
}
OpenPOWER on IntegriCloud