Support for UNIX-specific data
authorEric Biggers <ebiggers3@gmail.com>
Tue, 5 Mar 2013 20:22:04 +0000 (14:22 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Tue, 5 Mar 2013 20:22:04 +0000 (14:22 -0600)
14 files changed:
doc/imagex-apply.1.in
doc/imagex-capture.1.in
doc/imagex-export.1.in
doc/imagex-mount.1.in
programs/imagex.c
src/add_image.c
src/dentry.c
src/dentry.h
src/extract_image.c
src/mount_image.c
src/ntfs-apply.c
src/verify.c
src/wimlib.h
tests/test-imagex-mount

index 7621ef7..5d83d85 100644 (file)
@@ -56,7 +56,8 @@ In the normal mode of extraction, the following information will \fInot\fR be
 extracted from the WIM image(s):
 
 .IP \[bu] 4
-Security descriptors (file permissions)
+Security descriptors (file permissions) except through the extensions available
+through the \fB--unix-data\fR option
 .IP \[bu]
 The alternate (named) data streams for each file
 .IP \[bu]
@@ -79,7 +80,7 @@ empty for the intended use cases.  A new NTFS filesystem can be created using
 the \fBmkntfs\fR (8) command.
 
 The NTFS extraction mode is not available if wimlib was compiled using the
---without-ntfs-3g option.
+\fB--without-ntfs-3g\fR option.
 
 Please note that the NTFS extraction mode is \fInot\fR entered if \fITARGET\fR
 is a directory, even if a NTFS filesystem is mounted on \fITARGET\fR.  You must
@@ -171,7 +172,7 @@ included in the glob (but the first part MUST be specified as \fIWIMFILE\fR as
 well).
 
 Here's an example.  The names for the split WIMs usually go something like:
-       
+
 .RS
 .PP
 .nf
@@ -214,6 +215,14 @@ extracted, and some additional informational messages.
 \fB--ref\fR="\fIGLOB\fR"
 File glob of additional split WIM parts that are part of the split WIM being
 applied.  See \fBSPLIT_WIMS\fR.
+.TP
+\fB--unix-data\fR
+This option may only be given in the normal extraction mode (not NTFS).
+By default, in the normal extraction mode, \fBimagex apply\fR will ignore both
+Windows-style security descriptors and UNIX-specific file owners, groups, and
+modes set when using \fBimagex capture\fR with the \fB--unix-data\fR flag.  By
+passing \fB--unix-data\fR to \fBimagex apply\fR instead, this causes this
+UNIX-specific data to be restored when available.
 
 .SH NOTES
 
index b664cde..bf47b83 100644 (file)
@@ -56,7 +56,9 @@ information from the directory tree:
 .IP \[bu] 4
 File permissions.  The resulting WIM image will not contain any security
 descriptors because the format of the security descriptors is Windows-specific,
-and they cannot contain UNIX file modes.
+and they cannot contain UNIX file modes.  (Exception: see the \fB--unix-data\fR
+option.)
+
 .IP \[bu]
 No alternate data streams will be captured, since these do not exist on
 POSIX-compliant filesystems.  The resulting WIM image will not contain any
@@ -187,6 +189,14 @@ used:
 .RE
 .RE
 
+.TP
+\fB--unix-data\fR
+Store the UNIX owner, group, and mode of regular files, symbolic links, and
+directories.  This is done by adding a special alternate data stream to each
+directory entry that contains this information.  Please note that this flag is
+for convenience only, in case you want to use \fBimagex\fR to archive files on
+UNIX.  Microsoft's software will not understand this special information.
+
 .SH NOTES
 
 \fBimage append\fR does not support appending an image to a split WIM.
@@ -229,8 +239,8 @@ imagex capture somedir mywim.wim --compress=maximum --check
 Append an image to the WIM we just captured, but do it from a NTFS volume on the
 partition /dev/sda2, and name the image "Windows 7" and give it a description.
 You do not need to specify the compression type, because the WIM already is
-using LZX compression and this cannot be changed.  You need to specify --check
-if you don't want the integrity table to be discarded.
+using LZX compression and this cannot be changed.  You need to specify
+\fB--check\fR if you don't want the integrity table to be discarded.
 .RS
 .PP
 imagex append /dev/sda2 mywim.wim --check "Windows 7" "Warning: This operating
index 191a41c..52729a5 100644 (file)
@@ -92,7 +92,7 @@ included in the glob (but the first part MUST be specified as \fISRC_WIMFILE\fR
 well).
 
 Here's an example.  The names for the split WIMs usually go something like:
-       
+
 .RS
 .PP
 .nf
@@ -113,12 +113,12 @@ imagex export mywim.swm 1 other.wim --ref="mywim*.swm"
 
 .SH NOTES
 
-Unless --rebuild is specified, aborting an \fBimagex export\fR command mid-way
-through has a small chance of corrupting the destination WIM file.  However, a
-precaution is taken against this, so it should be very unlikely.  In the event
-of an aborted \fBimagex export\fR, \fBimagex optimize\fR can be run to remove
-extra data that may have been partially appended to the physical destination WIM
-file but not yet incorporated into the structure of the WIM.
+Unless \fB--rebuild\fR is specified, aborting an \fBimagex export\fR command
+mid-way through has a small chance of corrupting the destination WIM file.
+However, a precaution is taken against this, so it should be very unlikely.  In
+the event of an aborted \fBimagex export\fR, \fBimagex optimize\fR can be run to
+remove extra data that may have been partially appended to the physical
+destination WIM file but not yet incorporated into the structure of the WIM.
 
 .SH EXAMPLES
 Export the second image of 'boot.wim' to the new WIM file 'new.wim', and
index 82078d5..5a9928e 100644 (file)
@@ -3,11 +3,9 @@
 imagex-mount, imagex-mountrw, imagex-unmount \- Mount and unmount an image from a WIM archive
 
 .SH SYNOPSIS
-\fBimagex mount\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [--check]
-[--streams-interface=\fIINTERFACE\fR] [--ref="\fIGLOB\fR"]
+\fBimagex mount\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [\fIOPTION\fR]...
 .br
-\fBimagex mountrw\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [--check]
-[--streams-interface=\fIINTERFACE\fR] [--staging-dir=\fIDIR\fR]
+\fBimagex mountrw\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [\fIOPTION\fR]...
 .br
 \fBimagex unmount\fR \fIDIRECTORY\fR [--commit] [--check] [--rebuild]
 
@@ -43,7 +41,7 @@ included in the glob (but the first part MUST be specified as \fIWIMFILE\fR as
 well).
 
 Here's an example.  The names for the split WIMs usually go something like:
-       
+
 .RS
 .PP
 .nf
@@ -63,7 +61,7 @@ imagex mount mywim.swm 1 dir --ref="mywim*.swm"
 
 .SH NOTES
 
-If wimlib was configured using the --without-fuse flag, then the \fBimagex
+If wimlib was configured using the \fB--without-fuse\fR flag, then the \fBimagex
 mount\fR, \fBimagex mountrw\fR, and \fBimagex unmount\fR commands will not work.
 
 You can mount multiple images from a WIM file read-only at the same time, but
@@ -124,6 +122,24 @@ mountrw\fR.  See \fBSPLIT_WIMS\fR.
 \fB--staging-dir\fR=\fIDIR\fR
 Store temporary staging files in the directory \fIDIR\fR.  Only valid for
 \fBimagex mountrw\fR.
+.TP
+\fB--unix-data\fR
+By default, \fBimagex mount\fR and \fBimagex mountrw\fR will ignore both
+Windows-style security descriptors (which may have been set either from Windows or by
+\fBimagex capture\fR from a NTFS-volume) and UNIX-specific data (which is from using
+\fBimagex capture\fR with the \fB--unix-data\fR flag).  In this default mode,
+all files will simply be owned by the user running \fBimagex\fR and will have mode 0777.
+(Note: they will still not be accessible to other users unless you also specify
+\fB--allow-other\fR.)  If you instead provide the \fB--unix-data\fR flag, these
+default permissions will be overridden on a per-file basis with the
+UNIX-specific data when available, and in the case of \fBimagex mountrw\fR it
+will be possible to change the UNIX permissions using the standard UNIX
+tools and functions.
+.TP
+\fB--allow-other\fR
+Pass the \fBallow_other\fR option to the FUSE mount.  See \fBmount.fuse\fR (8).
+Note: to do this is a non-root user, \fBuser_allow_other\fR needs to be
+specified in /etc/fuse.conf (with the FUSE implementation on Linux, at least).
 
 .SH UNMOUNT OPTIONS
 
@@ -134,15 +150,15 @@ mount is read-only.
 .TP
 \fB--check\fR
 When writing \fIWIMFILE\fR, include an integrity table.  Has no effect if the
-mount is read-only or if --commit was not specified.
+mount is read-only or if \fB--commit\fR was not specified.
 .TP
 \fB--rebuild\fR
 Rebuild the entire WIM rather than appending any new data to the end of it.
 Rebuilding the WIM is slower, but will save a little bit of space that would
 otherwise be left as a hole in the WIM.  Even more space will be saved if the
 read-write mount resulted in streams being deleted from the WIM.  Also see
-\fBimagex optimize\fR.  Has no effect if the mount is read-only or if --commit
-was not specified.
+\fBimagex optimize\fR.  Has no effect if the mount is read-only or if
+\fB--commit\fR was not specified.
 
 .SH IMPLEMENTATION DETAILS
 
index 2a55a4e..e7ebc2e 100644 (file)
@@ -67,16 +67,16 @@ static const char *usage_strings[] = {
 "imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                     [DESCRIPTION] [--boot] [--check] [--flags EDITION_ID]\n"
 "                     [--verbose] [--dereference] [--config=FILE]\n"
-"                     [--threads=NUM_THREADS] [--rebuild]\n",
+"                     [--threads=NUM_THREADS] [--rebuild] [--unix-data]\n",
 [APPLY] =
 "imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
 "                    (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
-"                    [--symlink] [--verbose] [--ref=\"GLOB\"]\n",
+"                    [--symlink] [--verbose] [--ref=\"GLOB\"] [--unix-data]\n",
 [CAPTURE] =
 "imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n"
 "                      [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n"
 "                      [--flags EDITION_ID] [--verbose] [--dereference]\n"
-"                      [--config=FILE] [--threads=NUM_THREADS]\n",
+"                      [--config=FILE] [--threads=NUM_THREADS] [--unix-data]\n",
 [DELETE] =
 "imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check] [--soft]\n",
 [DIR] =
@@ -95,11 +95,11 @@ static const char *usage_strings[] = {
 [MOUNT] =
 "imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n"
 "                    [--check] [--debug] [--streams-interface=INTERFACE]\n"
-"                    [--ref=\"GLOB\"]\n",
+"                    [--ref=\"GLOB\"] [--unix-data] [--allow-other]\n",
 [MOUNTRW] =
 "imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
 "                      [--check] [--debug] [--streams-interface=INTERFACE]\n"
-"                      [--staging-dir=DIR]\n",
+"                      [--staging-dir=DIR] [--unix-data] [--allow-other]\n",
 [OPTIMIZE] =
 "imagex optimize WIMFILE [--check] [--recompress] [--compress=TYPE]\n",
 [SPLIT] =
@@ -115,11 +115,12 @@ static const struct option common_options[] = {
 };
 
 static const struct option apply_options[] = {
-       {"check",    no_argument,       NULL, 'c'},
-       {"hardlink", no_argument,       NULL, 'h'},
-       {"symlink",  no_argument,       NULL, 's'},
-       {"verbose",  no_argument,       NULL, 'v'},
-       {"ref",      required_argument, NULL, 'r'},
+       {"check",     no_argument,       NULL, 'c'},
+       {"hardlink",  no_argument,       NULL, 'h'},
+       {"symlink",   no_argument,       NULL, 's'},
+       {"verbose",   no_argument,       NULL, 'v'},
+       {"ref",       required_argument, NULL, 'r'},
+       {"unix-data", no_argument,       NULL, 'U'},
        {NULL, 0, NULL, 0},
 };
 static const struct option capture_or_append_options[] = {
@@ -132,6 +133,7 @@ static const struct option capture_or_append_options[] = {
        {"verbose",     no_argument,       NULL, 'v'},
        {"threads",     required_argument, NULL, 't'},
        {"rebuild",     no_argument,       NULL, 'R'},
+       {"unix-data",   no_argument,       NULL, 'U'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
@@ -172,6 +174,8 @@ static const struct option mount_options[] = {
        {"streams-interface", required_argument, NULL, 's'},
        {"ref",               required_argument, NULL, 'r'},
        {"staging-dir",       required_argument, NULL, 'D'},
+       {"unix-data",         no_argument,       NULL, 'U'},
+       {"allow-other",       no_argument,       NULL, 'A'},
        {NULL, 0, NULL, 0},
 };
 
@@ -575,6 +579,9 @@ static int imagex_apply(int argc, const char **argv)
                case 'r':
                        swm_glob = optarg;
                        break;
+               case 'U':
+                       extract_flags |= WIMLIB_EXTRACT_FLAG_UNIX_DATA;
+                       break;
                default:
                        usage(APPLY);
                        return -1;
@@ -707,6 +714,9 @@ static int imagex_capture_or_append(int argc, const char **argv)
                case 'R':
                        write_flags |= WIMLIB_WRITE_FLAG_REBUILD;
                        break;
+               case 'U':
+                       add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA;
+                       break;
                default:
                        usage(cmd);
                        return -1;
@@ -1418,6 +1428,9 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
 
        for_opt(c, mount_options) {
                switch (c) {
+               case 'A':
+                       mount_flags |= WIMLIB_MOUNT_FLAG_ALLOW_OTHER;
+                       break;
                case 'c':
                        open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
                        break;
@@ -1442,6 +1455,9 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
                case 'D':
                        staging_dir = optarg;
                        break;
+               case 'U':
+                       mount_flags |= WIMLIB_MOUNT_FLAG_UNIX_DATA;
+                       break;
                default:
                        goto mount_usage;
                }
index 62fbdfb..3e604e3 100644 (file)
@@ -230,10 +230,17 @@ static int build_dentry_tree(struct wim_dentry **root_ret,
        else
                inode->i_ino = (u64)root_stbuf.st_ino |
                                   ((u64)root_stbuf.st_dev << ((sizeof(ino_t) * 8) & 63));
-
-       add_image_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
        inode->i_resolved = 1;
-
+       if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) {
+               ret = inode_set_unix_data(inode, root_stbuf.st_uid,
+                                         root_stbuf.st_gid,
+                                         root_stbuf.st_mode,
+                                         lookup_table,
+                                         UNIX_DATA_ALL | UNIX_DATA_CREATE);
+               if (ret)
+                       goto out;
+       }
+       add_image_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
        if (S_ISREG(root_stbuf.st_mode)) { /* Archiving a regular file */
 
                struct wim_lookup_table_entry *lte;
@@ -635,10 +642,15 @@ WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *source,
 
        if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NTFS) {
 #ifdef WITH_NTFS_3G
-               if (add_image_flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
+               if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) {
                        ERROR("Cannot dereference files when capturing directly from NTFS");
                        return WIMLIB_ERR_INVALID_PARAM;
                }
+               if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) {
+                       ERROR("Capturing UNIX owner and mode not supported "
+                             "when capturing directly from NTFS");
+                       return WIMLIB_ERR_INVALID_PARAM;
+               }
                capture_tree = build_dentry_tree_ntfs;
                extra_arg = &w->ntfs_vol;
 #else
index 35a89d1..d7a684d 100644 (file)
@@ -1022,7 +1022,6 @@ bool dentry_add_child(struct wim_dentry * restrict parent,
        return true;
 }
 
-#ifdef WITH_FUSE
 /* Unlink a WIM dentry from the directory entry tree. */
 void unlink_dentry(struct wim_dentry *dentry)
 {
@@ -1031,10 +1030,8 @@ void unlink_dentry(struct wim_dentry *dentry)
                return;
        rb_erase(&dentry->rb_node, &parent->d_inode->i_children);
 }
-#endif
 
-#ifdef WITH_FUSE
-/* 
+/*
  * Returns the alternate data stream entry belonging to @inode that has the
  * stream name @stream_name.
  */
@@ -1057,9 +1054,7 @@ struct wim_ads_entry *inode_get_ads_entry(struct wim_inode *inode,
        }
        return NULL;
 }
-#endif
 
-#if defined(WITH_FUSE) || defined(WITH_NTFS_3G)
 /*
  * Add an alternate stream entry to a WIM inode and return a pointer to it, or
  * NULL if memory could not be allocated.
@@ -1094,9 +1089,55 @@ struct wim_ads_entry *inode_add_ads(struct wim_inode *inode, const char *stream_
        inode->i_num_ads = num_ads;
        return new_entry;
 }
-#endif
 
-#ifdef WITH_FUSE
+int inode_add_ads_with_data(struct wim_inode *inode, const char *name,
+                           const u8 *value, size_t size,
+                           struct wim_lookup_table *lookup_table)
+{
+       int ret = WIMLIB_ERR_NOMEM;
+       struct wim_ads_entry *new_ads_entry;
+       struct wim_lookup_table_entry *existing_lte;
+       struct wim_lookup_table_entry *lte;
+       u8 value_hash[SHA1_HASH_SIZE];
+
+       wimlib_assert(inode->i_resolved);
+       new_ads_entry = inode_add_ads(inode, name);
+       if (!new_ads_entry)
+               goto out;
+       sha1_buffer((const u8*)value, size, value_hash);
+       existing_lte = __lookup_resource(lookup_table, value_hash);
+       if (existing_lte) {
+               lte = existing_lte;
+               lte->refcnt++;
+       } else {
+               u8 *value_copy;
+               lte = new_lookup_table_entry();
+               if (!lte)
+                       goto out_free_ads_entry;
+               value_copy = MALLOC(size);
+               if (!value_copy) {
+                       FREE(lte);
+                       goto out_free_ads_entry;
+               }
+               memcpy(value_copy, value, size);
+               lte->resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
+               lte->attached_buffer              = value_copy;
+               lte->resource_entry.original_size = size;
+               lte->resource_entry.size          = size;
+               lte->resource_entry.flags         = 0;
+               copy_hash(lte->hash, value_hash);
+               lookup_table_insert(lookup_table, lte);
+       }
+       new_ads_entry->lte = lte;
+       ret = 0;
+       goto out;
+out_free_ads_entry:
+       inode_remove_ads(inode, new_ads_entry - inode->i_ads_entries,
+                        lookup_table);
+out:
+       return ret;
+}
+
 /* Remove an alternate data stream from a WIM inode  */
 void inode_remove_ads(struct wim_inode *inode, u16 idx,
                      struct wim_lookup_table *lookup_table)
@@ -1122,9 +1163,76 @@ void inode_remove_ads(struct wim_inode *inode, u16 idx,
                (inode->i_num_ads - idx - 1) * sizeof(inode->i_ads_entries[0]));
        inode->i_num_ads--;
 }
-#endif
 
+int inode_get_unix_data(const struct wim_inode *inode,
+                       struct wimlib_unix_data *unix_data,
+                       u16 *stream_idx_ret)
+{
+       const struct wim_ads_entry *ads_entry;
+       const struct wim_lookup_table_entry *lte;
+       size_t size;
+       int ret;
+
+       wimlib_assert(inode->i_resolved);
+
+       ads_entry = inode_get_ads_entry((struct wim_inode*)inode,
+                                       WIMLIB_UNIX_DATA_TAG, NULL);
+       if (!ads_entry)
+               return NO_UNIX_DATA;
 
+       if (stream_idx_ret)
+               *stream_idx_ret = ads_entry - inode->i_ads_entries;
+
+       lte = ads_entry->lte;
+       if (!lte)
+               return NO_UNIX_DATA;
+
+       size = wim_resource_size(lte);
+       if (size != sizeof(struct wimlib_unix_data))
+               return BAD_UNIX_DATA;
+
+       ret = read_full_wim_resource(lte, (u8*)unix_data, 0);
+       if (ret)
+               return ret;
+
+       if (unix_data->version != 0)
+               return BAD_UNIX_DATA;
+       return 0;
+}
+
+int inode_set_unix_data(struct wim_inode *inode,
+                       uid_t uid, gid_t gid, mode_t mode,
+                       struct wim_lookup_table *lookup_table,
+                       int which)
+{
+       struct wimlib_unix_data unix_data;
+       int ret;
+       bool have_good_unix_data = false;
+       bool have_unix_data = false;
+       u16 stream_idx;
+
+       if (!(which & UNIX_DATA_CREATE)) {
+               ret = inode_get_unix_data(inode, &unix_data, &stream_idx);
+               if (ret == 0 || ret == BAD_UNIX_DATA || ret > 0)
+                       have_unix_data = true;
+               if (ret == 0)
+                       have_good_unix_data = true;
+       }
+       unix_data.version = 0;
+       if (which & UNIX_DATA_UID || !have_good_unix_data)
+               unix_data.uid = uid;
+       if (which & UNIX_DATA_GID || !have_good_unix_data)
+               unix_data.gid = gid;
+       if (which & UNIX_DATA_MODE || !have_good_unix_data)
+               unix_data.mode = mode;
+       ret = inode_add_ads_with_data(inode, WIMLIB_UNIX_DATA_TAG,
+                                     (const u8*)&unix_data,
+                                     sizeof(struct wimlib_unix_data),
+                                     lookup_table);
+       if (ret == 0 && have_unix_data)
+               inode_remove_ads(inode, stream_idx, lookup_table);
+       return ret;
+}
 
 /*
  * Reads the alternate data stream entries of a WIM dentry.
index 99de40a..5517c92 100644 (file)
@@ -396,10 +396,41 @@ extern struct wim_ads_entry *inode_get_ads_entry(struct wim_inode *inode,
 
 extern struct wim_ads_entry *inode_add_ads(struct wim_inode *dentry,
                                           const char *stream_name);
+extern int inode_add_ads_with_data(struct wim_inode *inode, const char *name,
+                                  const u8 *value, size_t size,
+                                  struct wim_lookup_table *lookup_table);
 
 extern void inode_remove_ads(struct wim_inode *inode, u16 idx,
                             struct wim_lookup_table *lookup_table);
 
+#define WIMLIB_UNIX_DATA_TAG "$$__wimlib_UNIX_data"
+
+#define WIMLIB_UNIX_DATA_TAG_LEN (sizeof(WIMLIB_UNIX_DATA_TAG) - 1)
+
+/* Format for special alternate data stream entries to store UNIX data for files
+ * and directories (see: WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
+struct wimlib_unix_data {
+       u16 version; /* Must be 0 */
+       u16 uid;
+       u16 gid;
+       u16 mode;
+} PACKED;
+#define NO_UNIX_DATA (-1)
+#define BAD_UNIX_DATA (-2)
+extern int inode_get_unix_data(const struct wim_inode *inode,
+                              struct wimlib_unix_data *unix_data,
+                              u16 *stream_idx_ret);
+
+#define UNIX_DATA_UID    0x1
+#define UNIX_DATA_GID    0x2
+#define UNIX_DATA_MODE   0x4
+#define UNIX_DATA_ALL    (UNIX_DATA_UID | UNIX_DATA_GID | UNIX_DATA_MODE)
+#define UNIX_DATA_CREATE 0x8
+extern int inode_set_unix_data(struct wim_inode *inode,
+                              uid_t uid, gid_t gid, mode_t mode,
+                              struct wim_lookup_table *lookup_table,
+                              int which);
+
 extern int read_dentry(const u8 metadata_resource[], u64 metadata_resource_len,
                       u64 offset, struct wim_dentry *dentry);
 
index c202156..96c9294 100644 (file)
@@ -114,6 +114,63 @@ static int extract_regular_file_linked(struct wim_dentry *dentry,
        return 0;
 }
 
+static int symlink_apply_unix_data(const char *link,
+                                  const struct wimlib_unix_data *unix_data)
+{
+       if (lchown(link, unix_data->uid, unix_data->gid)) {
+               if (errno == EPERM) {
+                       /* Ignore */
+                       WARNING_WITH_ERRNO("failed to set symlink UNIX owner/group");
+               } else {
+                       ERROR_WITH_ERRNO("failed to set symlink UNIX owner/group");
+                       return WIMLIB_ERR_INVALID_DENTRY;
+               }
+       }
+       return 0;
+}
+
+static int fd_apply_unix_data(int fd, const struct wimlib_unix_data *unix_data)
+{
+       if (fchown(fd, unix_data->uid, unix_data->gid)) {
+               if (errno == EPERM) {
+                       WARNING_WITH_ERRNO("failed to set file UNIX owner/group");
+                       /* Ignore? */
+               } else {
+                       ERROR_WITH_ERRNO("failed to set file UNIX owner/group");
+                       return WIMLIB_ERR_INVALID_DENTRY;
+               }
+       }
+
+       if (fchmod(fd, unix_data->mode)) {
+               if (errno == EPERM) {
+                       WARNING_WITH_ERRNO("failed to set UNIX file mode");
+                       /* Ignore? */
+               } else {
+                       ERROR_WITH_ERRNO("failed to set UNIX file mode");
+                       return WIMLIB_ERR_INVALID_DENTRY;
+               }
+       }
+       return 0;
+}
+
+static int dir_apply_unix_data(const char *dir,
+                              const struct wimlib_unix_data *unix_data)
+{
+       int dfd = open(dir, O_RDONLY);
+       int ret;
+       if (dfd >= 0) {
+               ret = fd_apply_unix_data(dfd, unix_data);
+               if (close(dfd)) {
+                       ERROR_WITH_ERRNO("can't close directory `%s'", dir);
+                       ret = WIMLIB_ERR_MKDIR;
+               }
+       } else {
+               ERROR_WITH_ERRNO("can't open directory `%s'", dir);
+               ret = WIMLIB_ERR_MKDIR;
+       }
+       return ret;
+}
+
 static int extract_regular_file_unlinked(struct wim_dentry *dentry,
                                         struct apply_args *args,
                                         const char *output_path,
@@ -171,7 +228,7 @@ static int extract_regular_file_unlinked(struct wim_dentry *dentry,
                /* Empty file with no lookup table entry */
                DEBUG("Empty file `%s'.", output_path);
                ret = 0;
-               goto out;
+               goto out_extract_unix_data;
        }
 
        ret = extract_wim_resource_to_fd(lte, out_fd, wim_resource_size(lte));
@@ -179,11 +236,27 @@ static int extract_regular_file_unlinked(struct wim_dentry *dentry,
                ERROR("Failed to extract resource to `%s'", output_path);
                goto out;
        }
-       args->progress.extract.completed_bytes += wim_resource_size(lte);
+
+out_extract_unix_data:
+       if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
+               struct wimlib_unix_data unix_data;
+               ret = inode_get_unix_data(inode, &unix_data, NULL);
+               if (ret > 0)
+                       ;
+               else if (ret < 0)
+                       ret = 0;
+               else
+                       ret = fd_apply_unix_data(out_fd, &unix_data);
+               if (ret != 0)
+                       goto out;
+       }
+       if (lte)
+               args->progress.extract.completed_bytes += wim_resource_size(lte);
 out:
        if (close(out_fd) != 0) {
                ERROR_WITH_ERRNO("Failed to close file `%s'", output_path);
-               ret = WIMLIB_ERR_WRITE;
+               if (ret == 0)
+                       ret = WIMLIB_ERR_WRITE;
        }
        return ret;
 }
@@ -232,20 +305,35 @@ static int extract_symlink(struct wim_dentry *dentry,
                return WIMLIB_ERR_LINK;
        }
        lte = inode_unnamed_lte_resolved(dentry->d_inode);
+       wimlib_assert(lte != NULL);
+       if (args->extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
+               struct wimlib_unix_data unix_data;
+               ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL);
+               if (ret > 0)
+                       ;
+               else if (ret < 0)
+                       ret = 0;
+               else
+                       ret = symlink_apply_unix_data(output_path, &unix_data);
+               if (ret != 0)
+                       return ret;
+       }
        args->progress.extract.completed_bytes += wim_resource_size(lte);
        return 0;
 }
 
-static int extract_directory(const char *output_path, bool is_root)
+static int extract_directory(struct wim_dentry *dentry,
+                            const char *output_path, bool is_root)
 {
        int ret;
        struct stat stbuf;
+
        ret = stat(output_path, &stbuf);
        if (ret == 0) {
                if (S_ISDIR(stbuf.st_mode)) {
                        /*if (!is_root)*/
                                /*WARNING("`%s' already exists", output_path);*/
-                       return 0;
+                       goto dir_exists;
                } else {
                        ERROR("`%s' is not a directory", output_path);
                        return WIMLIB_ERR_MKDIR;
@@ -262,7 +350,20 @@ static int extract_directory(const char *output_path, bool is_root)
                                 output_path);
                return WIMLIB_ERR_MKDIR;
        }
-       return 0;
+dir_exists:
+       if (dentry) {
+               struct wimlib_unix_data unix_data;
+               ret = inode_get_unix_data(dentry->d_inode, &unix_data, NULL);
+               if (ret > 0)
+                       ;
+               else if (ret < 0)
+                       ret = 0;
+               else
+                       ret = dir_apply_unix_data(output_path, &unix_data);
+       } else {
+               ret = 0;
+       }
+       return ret;
 }
 
 /* Extracts a file, directory, or symbolic link from the WIM archive. */
@@ -281,7 +382,9 @@ static int apply_dentry_normal(struct wim_dentry *dentry, void *arg)
        if (inode_is_symlink(inode))
                return extract_symlink(dentry, args, output_path);
        else if (inode_is_directory(inode))
-               return extract_directory(output_path, false);
+               return extract_directory((args->extract_flags &
+                                          WIMLIB_EXTRACT_FLAG_UNIX_DATA) ? dentry : NULL,
+                                        output_path, false);
        else
                return extract_regular_file(dentry, args, output_path);
 }
@@ -702,7 +805,7 @@ static int extract_all_images(WIMStruct *w, const char *target,
        int image;
        const char *image_name;
 
-       ret = extract_directory(target, true);
+       ret = extract_directory(NULL, target, true);
        if (ret != 0)
                return ret;
 
@@ -758,6 +861,10 @@ WIMLIBAPI int wimlib_extract_image(WIMStruct *w,
                              "directly to a NTFS volume");
                        return WIMLIB_ERR_INVALID_PARAM;
                }
+               if (extract_flags & WIMLIB_EXTRACT_FLAG_UNIX_DATA) {
+                       ERROR("Cannot restore UNIX-specific data in the NTFS extraction mode");
+                       return WIMLIB_ERR_INVALID_PARAM;
+               }
 #else
                ERROR("wimlib was compiled without support for NTFS-3g, so");
                ERROR("we cannot apply a WIM image directly to a NTFS volume");
index af39246..b9b6345 100644 (file)
@@ -106,6 +106,9 @@ struct wimfs_context {
        char *daemon_to_unmount_mq_name;
        mqd_t unmount_to_daemon_mq;
        mqd_t daemon_to_unmount_mq;
+
+       uid_t default_uid;
+       gid_t default_gid;
 };
 
 static void init_wimfs_context(struct wimfs_context *ctx)
@@ -116,9 +119,11 @@ static void init_wimfs_context(struct wimfs_context *ctx)
        INIT_LIST_HEAD(&ctx->staging_list);
 }
 
+#define WIMFS_CTX(fuse_ctx) ((struct wimfs_context*)(fuse_ctx)->private_data)
+
 static inline struct wimfs_context *wimfs_get_context()
 {
-       return (struct wimfs_context*)fuse_get_context()->private_data;
+       return WIMFS_CTX(fuse_get_context());
 }
 
 static inline WIMStruct *wimfs_get_WIMStruct()
@@ -280,20 +285,18 @@ static int close_wimfs_fd(struct wimfs_fd *fd)
 /*
  * Add a new dentry with a new inode to a WIM image.
  *
- * @ctx:        Context for the mounted WIM image.
- * @path:       Path to create the dentry at.
- * @dentry_ret:  Return the pointer to the dentry if successful.
- *
  * Returns 0 on success, or negative error number on failure.
  */
-static int create_dentry(struct wimfs_context *ctx, const char *path,
+static int create_dentry(struct fuse_context *fuse_ctx,const char *path,
+                        mode_t mode, int attributes,
                         struct wim_dentry **dentry_ret)
 {
        struct wim_dentry *parent;
        struct wim_dentry *new;
        const char *basename;
+       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
 
-       parent = get_parent_dentry(ctx->wim, path);
+       parent = get_parent_dentry(wimfs_ctx->wim, path);
        if (!parent)
                return -errno;
 
@@ -309,10 +312,25 @@ static int create_dentry(struct wimfs_context *ctx, const char *path,
                return -errno;
 
        new->d_inode->i_resolved = 1;
-       new->d_inode->i_ino = ctx->next_ino++;
+       new->d_inode->i_ino = wimfs_ctx->next_ino++;
+       new->d_inode->i_attributes = attributes;
+
+       if (wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
+               if (inode_set_unix_data(new->d_inode,
+                                       fuse_ctx->uid,
+                                       fuse_ctx->gid,
+                                       mode & ~fuse_ctx->umask,
+                                       wimfs_ctx->wim->lookup_table,
+                                       UNIX_DATA_ALL | UNIX_DATA_CREATE))
+               {
+                       free_dentry(new);
+                       return -ENOMEM;
+               }
+       }
        dentry_add_child(parent, new);
-       hlist_add_head(&new->d_inode->i_hlist, ctx->image_inode_list);
-       *dentry_ret = new;
+       hlist_add_head(&new->d_inode->i_hlist, wimfs_ctx->image_inode_list);
+       if (dentry_ret)
+               *dentry_ret = new;
        return 0;
 }
 
@@ -344,6 +362,16 @@ static void remove_dentry(struct wim_dentry *dentry,
        put_dentry(dentry);
 }
 
+static mode_t inode_default_unix_mode(const struct wim_inode *inode)
+{
+       if (inode_is_symlink(inode))
+               return S_IFLNK | 0777;
+       else if (inode_is_directory(inode))
+               return S_IFDIR | 0777;
+       else
+               return S_IFREG | 0777;
+}
+
 /* Transfers file attributes from a struct wim_inode to a `stat' buffer.
  *
  * The lookup table entry tells us which stream in the inode we are statting.
@@ -353,18 +381,22 @@ static int inode_to_stbuf(const struct wim_inode *inode,
                          const struct wim_lookup_table_entry *lte,
                          struct stat *stbuf)
 {
-       if (inode_is_symlink(inode))
-               stbuf->st_mode = S_IFLNK | 0777;
-       else if (inode_is_directory(inode))
-               stbuf->st_mode = S_IFDIR | 0755;
-       else
-               stbuf->st_mode = S_IFREG | 0755;
-
-       stbuf->st_ino   = (ino_t)inode->i_ino;
+       const struct wimfs_context *ctx = wimfs_get_context();
+
+       memset(stbuf, 0, sizeof(struct stat));
+       stbuf->st_mode = inode_default_unix_mode(inode);
+       stbuf->st_uid = ctx->default_uid;
+       stbuf->st_gid = ctx->default_gid;
+       if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA) {
+               struct wimlib_unix_data unix_data;
+               if (inode_get_unix_data(inode, &unix_data, NULL) == 0) {
+                       stbuf->st_uid = unix_data.uid;
+                       stbuf->st_gid = unix_data.gid;
+                       stbuf->st_mode = unix_data.mode;
+               }
+       }
+       stbuf->st_ino = (ino_t)inode->i_ino;
        stbuf->st_nlink = inode->i_nlink;
-       stbuf->st_uid   = getuid();
-       stbuf->st_gid   = getgid();
-
        if (lte) {
                if (lte->resource_location == RESOURCE_IN_STAGING_FILE) {
                        struct stat native_stat;
@@ -381,10 +413,10 @@ static int inode_to_stbuf(const struct wim_inode *inode,
                stbuf->st_size = 0;
        }
 
-       stbuf->st_atime   = wim_timestamp_to_unix(inode->i_last_access_time);
-       stbuf->st_mtime   = wim_timestamp_to_unix(inode->i_last_write_time);
-       stbuf->st_ctime   = wim_timestamp_to_unix(inode->i_creation_time);
-       stbuf->st_blocks  = (stbuf->st_size + 511) / 512;
+       stbuf->st_atime = wim_timestamp_to_unix(inode->i_last_access_time);
+       stbuf->st_mtime = wim_timestamp_to_unix(inode->i_last_write_time);
+       stbuf->st_ctime = wim_timestamp_to_unix(inode->i_creation_time);
+       stbuf->st_blocks = (stbuf->st_size + 511) / 512;
        return 0;
 }
 
@@ -896,31 +928,44 @@ static int open_message_queues(struct wimfs_context *ctx, bool daemon)
 {
        int unmount_to_daemon_mq_flags = O_WRONLY | O_CREAT;
        int daemon_to_unmount_mq_flags = O_RDONLY | O_CREAT;
+       mode_t mode;
+       mode_t orig_umask;
+       int ret;
 
-       if (daemon)
+       if (daemon) {
                swap(unmount_to_daemon_mq_flags, daemon_to_unmount_mq_flags);
+               mode = 0600;
+       } else {
+               mode = 0666;
+       }
 
+       orig_umask = umask(0000);
        DEBUG("Opening message queue \"%s\"", ctx->unmount_to_daemon_mq_name);
        ctx->unmount_to_daemon_mq = mq_open(ctx->unmount_to_daemon_mq_name,
-                                           unmount_to_daemon_mq_flags, 0700, NULL);
+                                           unmount_to_daemon_mq_flags, mode, NULL);
 
        if (ctx->unmount_to_daemon_mq == (mqd_t)-1) {
                ERROR_WITH_ERRNO("mq_open()");
-               return WIMLIB_ERR_MQUEUE;
+               ret = WIMLIB_ERR_MQUEUE;
+               goto out;
        }
 
        DEBUG("Opening message queue \"%s\"", ctx->daemon_to_unmount_mq_name);
        ctx->daemon_to_unmount_mq = mq_open(ctx->daemon_to_unmount_mq_name,
-                                           daemon_to_unmount_mq_flags, 0700, NULL);
+                                           daemon_to_unmount_mq_flags, mode, NULL);
 
        if (ctx->daemon_to_unmount_mq == (mqd_t)-1) {
                ERROR_WITH_ERRNO("mq_open()");
                mq_close(ctx->unmount_to_daemon_mq);
                mq_unlink(ctx->unmount_to_daemon_mq_name);
                ctx->unmount_to_daemon_mq = (mqd_t)-1;
-               return WIMLIB_ERR_MQUEUE;
+               ret = WIMLIB_ERR_MQUEUE;
+               goto out;
        }
-       return 0;
+       ret = 0;
+out:
+       umask(orig_umask);
+       return ret;
 }
 
 /* Try to determine the maximum message size of a message queue.  The return
@@ -1458,31 +1503,52 @@ static int execute_fusermount(const char *dir)
        return 0;
 }
 
+#if 0
 static int wimfs_access(const char *path, int mask)
 {
-       /* Permissions not implemented */
-       return 0;
+       return -ENOSYS;
 }
+#endif
 
 static int wimfs_chmod(const char *path, mode_t mask)
 {
        struct wim_dentry *dentry;
        struct wimfs_context *ctx = wimfs_get_context();
-       struct wim_inode *inode;
-       struct stat stbuf;
        int ret;
 
-       ret = lookup_resource(ctx->wim, path,
-                             get_lookup_flags(ctx) | LOOKUP_FLAG_DIRECTORY_OK,
+       if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
+               return -EPERM;
+
+       ret = lookup_resource(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
                              &dentry, NULL, NULL);
-       if (ret != 0)
+       if (ret)
                return ret;
-       inode = dentry->d_inode;
-       inode_to_stbuf(inode, NULL, &stbuf);
-       if (mask == stbuf.st_mode)
-               return 0;
-       else
+
+       ret = inode_set_unix_data(dentry->d_inode, ctx->default_uid,
+                                 ctx->default_gid, mask,
+                                 ctx->wim->lookup_table, UNIX_DATA_MODE);
+       return ret ? -ENOMEM : 0;
+}
+
+static int wimfs_chown(const char *path, uid_t uid, gid_t gid)
+{
+       struct wim_dentry *dentry;
+       struct wimfs_context *ctx = wimfs_get_context();
+       int ret;
+
+       if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_UNIX_DATA))
                return -EPERM;
+
+       ret = lookup_resource(ctx->wim, path, LOOKUP_FLAG_DIRECTORY_OK,
+                             &dentry, NULL, NULL);
+       if (ret)
+               return ret;
+
+       ret = inode_set_unix_data(dentry->d_inode, uid, gid,
+                                 inode_default_unix_mode(dentry->d_inode),
+                                 ctx->wim->lookup_table,
+                                 UNIX_DATA_UID | UNIX_DATA_GID);
+       return ret ? -ENOMEM : 0;
 }
 
 /* Called when the filesystem is unmounted. */
@@ -1679,26 +1745,24 @@ static int wimfs_listxattr(const char *path, char *list, size_t size)
 #endif
 
 
-/* Create a directory in the WIM.
- * @mode is currently ignored.  */
+/* Create a directory in the WIM image. */
 static int wimfs_mkdir(const char *path, mode_t mode)
 {
-       struct wim_dentry *dentry;
-       int ret;
-
-       ret = create_dentry(wimfs_get_context(), path, &dentry);
-       if (ret == 0)
-               dentry->d_inode->i_attributes = FILE_ATTRIBUTE_DIRECTORY;
-       return ret;
+       return create_dentry(fuse_get_context(), path, mode | S_IFDIR,
+                            FILE_ATTRIBUTE_DIRECTORY, NULL);
 }
 
-/* Create a regular file in the WIM.
- * @mode is currently ignored.  */
+/* Create a regular file or alternate data stream in the WIM image. */
 static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
 {
        const char *stream_name;
-       struct wimfs_context *ctx = wimfs_get_context();
-       if ((ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
+       struct fuse_context *fuse_ctx = fuse_get_context();
+       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
+
+       if (!S_ISREG(mode))
+               return -EPERM;
+
+       if ((wimfs_ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
             && (stream_name = path_stream_name(path))) {
                /* Make an alternate data stream */
                struct wim_ads_entry *new_entry;
@@ -1708,7 +1772,7 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                wimlib_assert(*p == ':');
                *p = '\0';
 
-               inode = wim_pathname_to_inode(ctx->wim, path);
+               inode = wim_pathname_to_inode(wimfs_ctx->wim, path);
                if (!inode)
                        return -errno;
                if (inode->i_attributes &
@@ -1721,14 +1785,9 @@ static int wimfs_mknod(const char *path, mode_t mode, dev_t rdev)
                        return -ENOMEM;
                return 0;
        } else {
-               struct wim_dentry *dentry;
-               int ret;
-
                /* Make a normal file (not an alternate data stream) */
-               ret = create_dentry(ctx, path, &dentry);
-               if (ret == 0)
-                       dentry->d_inode->i_attributes = FILE_ATTRIBUTE_NORMAL;
-               return ret;
+               return create_dentry(fuse_ctx, path, mode | S_IFREG,
+                                    FILE_ATTRIBUTE_NORMAL, NULL);
        }
 }
 
@@ -2010,7 +2069,7 @@ static int wimfs_rename(const char *from, const char *to)
 /* Remove a directory */
 static int wimfs_rmdir(const char *path)
 {
-       struct wim_dentry *parent, *dentry;
+       struct wim_dentry *dentry;
        WIMStruct *w = wimfs_get_WIMStruct();
 
        dentry = get_dentry(w, path);
@@ -2033,13 +2092,10 @@ static int wimfs_setxattr(const char *path, const char *name,
                          const char *value, size_t size, int flags)
 {
        struct wim_ads_entry *existing_ads_entry;
-       struct wim_ads_entry *new_ads_entry;
-       struct wim_lookup_table_entry *existing_lte;
-       struct wim_lookup_table_entry *lte;
        struct wim_inode *inode;
-       u8 value_hash[SHA1_HASH_SIZE];
        u16 ads_idx;
        struct wimfs_context *ctx = wimfs_get_context();
+       int ret;
 
        if (!(ctx->mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR))
                return -ENOTSUP;
@@ -2061,56 +2117,28 @@ static int wimfs_setxattr(const char *path, const char *name,
                if (flags & XATTR_REPLACE)
                        return -ENOATTR;
        }
-       new_ads_entry = inode_add_ads(inode, name);
-       if (!new_ads_entry)
-               return -ENOMEM;
-
-       sha1_buffer((const u8*)value, size, value_hash);
 
-       existing_lte = __lookup_resource(ctx->wim->lookup_table, value_hash);
-
-       if (existing_lte) {
-               lte = existing_lte;
-               lte->refcnt++;
-       } else {
-               u8 *value_copy;
-               lte = new_lookup_table_entry();
-               if (!lte)
-                       return -ENOMEM;
-               value_copy = MALLOC(size);
-               if (!value_copy) {
-                       FREE(lte);
-                       return -ENOMEM;
-               }
-               memcpy(value_copy, value, size);
-               lte->resource_location            = RESOURCE_IN_ATTACHED_BUFFER;
-               lte->attached_buffer              = value_copy;
-               lte->resource_entry.original_size = size;
-               lte->resource_entry.size          = size;
-               lte->resource_entry.flags         = 0;
-               copy_hash(lte->hash, value_hash);
-               lookup_table_insert(ctx->wim->lookup_table, lte);
-       }
-       new_ads_entry->lte = lte;
-       return 0;
+       ret = inode_add_ads_with_data(inode, name, (const u8*)value,
+                                     size, ctx->wim->lookup_table);
+       return ret ? -ENOMEM : 0;
 }
 #endif
 
 static int wimfs_symlink(const char *to, const char *from)
 {
-       struct wimfs_context *ctx = wimfs_get_context();
+       struct fuse_context *fuse_ctx = fuse_get_context();
+       struct wimfs_context *wimfs_ctx = WIMFS_CTX(fuse_ctx);
        struct wim_dentry *dentry;
        int ret;
 
-       ret = create_dentry(ctx, from, &dentry);
+       ret = create_dentry(fuse_ctx, from, S_IFLNK | 0777,
+                           FILE_ATTRIBUTE_REPARSE_POINT, &dentry);
        if (ret == 0) {
-               dentry->d_inode->i_attributes = FILE_ATTRIBUTE_REPARSE_POINT;
                dentry->d_inode->i_reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
                if (inode_set_symlink(dentry->d_inode, to,
-                                     ctx->wim->lookup_table, NULL))
+                                     wimfs_ctx->wim->lookup_table, NULL))
                {
-                       unlink_dentry(dentry);
-                       free_dentry(dentry);
+                       remove_dentry(dentry, wimfs_ctx->wim->lookup_table);
                        ret = -ENOMEM;
                }
        }
@@ -2263,8 +2291,11 @@ static int wimfs_write(const char *path, const char *buf, size_t size,
 }
 
 static struct fuse_operations wimfs_operations = {
+#if 0
        .access      = wimfs_access,
+#endif
        .chmod       = wimfs_chmod,
+       .chown       = wimfs_chown,
        .destroy     = wimfs_destroy,
 #if 0
        .fallocate   = wimfs_fallocate,
@@ -2409,7 +2440,8 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        ctx.wim = wim;
        ctx.mount_flags = mount_flags;
        ctx.image_inode_list = &imd->inode_list;
-
+       ctx.default_uid = getuid();
+       ctx.default_gid = getgid();
        if (mount_flags & WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)
                ctx.default_lookup_flags = LOOKUP_FLAG_ADS_OK;
 
@@ -2446,6 +2478,7 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
 #if FUSE_MAJOR_VERSION > 2 || (FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 8)
                ",hard_remove"
 #endif
+               ",default_permissions"
                ;
        argv[argc++] = "-o";
        argv[argc++] = optstring;
@@ -2458,6 +2491,8 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
                /* Read-only mount */
                strcat(optstring, ",ro");
        }
+       if (mount_flags & WIMLIB_MOUNT_FLAG_ALLOW_OTHER)
+               strcat(optstring, ",allow_other");
        argv[argc] = NULL;
 
 #ifdef ENABLE_DEBUG
index fcf141f..fc71b9b 100644 (file)
@@ -88,6 +88,7 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct wim_dentry *dent
        unsigned stream_idx = 0;
        ntfschar *stream_name = AT_UNNAMED;
        u32 stream_name_len = 0;
+       const char *stream_name_utf8;
        const struct wim_inode *inode = dentry->d_inode;
        struct wim_lookup_table_entry *lte;
 
@@ -99,6 +100,15 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct wim_dentry *dent
        lte = inode->i_lte;
        while (1) {
                if (stream_name_len) {
+
+                       /* Skip special UNIX data entries (see documentation for
+                        * WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) */
+                       if (stream_name_len == WIMLIB_UNIX_DATA_TAG_LEN
+                           && !memcmp(stream_name_utf8,
+                                      WIMLIB_UNIX_DATA_TAG,
+                                      WIMLIB_UNIX_DATA_TAG_LEN))
+                               goto cont;
+
                        /* Create an empty named stream. */
                        ret = ntfs_attr_add(ni, AT_DATA, stream_name,
                                            stream_name_len, NULL, 0);
@@ -148,11 +158,13 @@ static int write_ntfs_data_streams(ntfs_inode *ni, const struct wim_dentry *dent
                         * have been extracted. */
                        progress_info->extract.completed_bytes += wim_resource_size(lte);
                }
+       cont:
                if (stream_idx == inode->i_num_ads) /* Has the last stream been extracted? */
                        break;
 
                /* Get the name and lookup table entry for the next stream. */
                stream_name = (ntfschar*)inode->i_ads_entries[stream_idx].stream_name;
+               stream_name_utf8 = inode->i_ads_entries[stream_idx].stream_name_utf8;
                stream_name_len = inode->i_ads_entries[stream_idx].stream_name_len / 2;
                lte = inode->i_ads_entries[stream_idx].lte;
                stream_idx++;
index 777a6e0..bfb3577 100644 (file)
@@ -57,7 +57,7 @@ static int verify_inode(struct wim_inode *inode, const WIMStruct *w)
 
        /* Check that lookup table entries for all the inode's stream exist,
         * except if the SHA1 message digest is all 0's, which indicates an
-        * empty stream. 
+        * empty stream.
         *
         * This check is skipped on split WIMs. */
        if (w->hdr.total_parts == 1) {
index 55289fe..294ea19 100644 (file)
  * message being printed.
  *
  * wimlib is thread-safe as long as different ::WIMStruct's are used, except for
- * the fact that wimlib_set_print_errors() and wimlib_set_memory_allocator()
- * both apply globally, and you also must call wimlib_global_init() in the main
- * thread to avoid any race conditions with one-time allocations of memory.
+ * the following exceptions:
+ * - wimlib_set_print_errors() and wimlib_set_memory_allocator() both apply globally.
+ * - You also must call wimlib_global_init() in the main thread to avoid any
+ *   race conditions with one-time allocations of memory.
+ * - wimlib_mount_image(), while it can be used to mount multiple WIMs
+ *   concurrently in the same process, will daemonize the entire process when it
+ *   does so for the first time.  This includes changing the working directory
+ *   to the root directory.
  *
  * To open an existing WIM, use wimlib_open_wim().
  *
 #include <stdbool.h>
 #include <inttypes.h>
 
+/** Major version of the library (for example, the 1 in 1.2.5). */
 #define WIMLIB_MAJOR_VERSION 1
+
+/** Minor version of the library (for example, the 2 in 1.2.5). */
 #define WIMLIB_MINOR_VERSION 2
+
+/** Patch version of the library (for example, the 5 in 1.2.5). */
 #define WIMLIB_PATCH_VERSION 5
 
 /**
@@ -547,7 +557,9 @@ typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type,
  * WIMLIB_ADD_IMAGE_FLAG_*   *
  *****************************/
 
-/** Directly capture a NTFS volume rather than a generic directory */
+/** Directly capture a NTFS volume rather than a generic directory.  This flag
+ * cannot be combined with ::WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE or
+ * ::WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA.   */
 #define WIMLIB_ADD_IMAGE_FLAG_NTFS                     0x00000001
 
 /** Follow symlinks; archive and dump the files they point to.  Cannot be used
@@ -562,6 +574,14 @@ typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type,
 /** Mark the image being added as the bootable image of the WIM. */
 #define WIMLIB_ADD_IMAGE_FLAG_BOOT                     0x00000008
 
+/** Store the UNIX owner, group, and mode.  This is done by adding a special
+ * alternate data stream to each regular file, symbolic link, and directory to
+ * contain this information.  Please note that this flag is for convenience
+ * only; Microsoft's version of imagex.exe will not understand this special
+ * information.  This flag cannot be combined with ::WIMLIB_ADD_IMAGE_FLAG_NTFS.
+ * */
+#define WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA                        0x00000010
+
 /******************************
  * WIMLIB_EXPORT_FLAG_* *
  ******************************/
@@ -594,6 +614,10 @@ typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type,
 /** Read the WIM file sequentially while extracting the image. */
 #define WIMLIB_EXTRACT_FLAG_SEQUENTIAL                 0x00000010
 
+/** Extract special UNIX data captured with ::WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA.
+ * Cannot be used with ::WIMLIB_EXTRACT_FLAG_NTFS. */
+#define WIMLIB_EXTRACT_FLAG_UNIX_DATA                  0x00000020
+
 /******************************
  * WIMLIB_MOUNT_FLAG_*        *
  ******************************/
@@ -615,6 +639,14 @@ typedef int (*wimlib_progress_func_t)(enum wimlib_progress_msg msg_type,
  * file name, a colon, then the alternate file stream name. */
 #define WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS     0x00000010
 
+/** Use UNIX file owners, groups, and modes if available in the WIM (see
+ * ::WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA). */
+#define WIMLIB_MOUNT_FLAG_UNIX_DATA                    0x00000020
+
+/** Allow other users to see the mounted filesystem.  (this passes the @c
+ * allow_other option to FUSE mount) */
+#define WIMLIB_MOUNT_FLAG_ALLOW_OTHER                  0x00000040
+
 /******************************
  * WIMLIB_OPEN_FLAG_*         *
  ******************************/
@@ -1090,11 +1122,13 @@ extern int wimlib_export_image(WIMStruct *src_wim, int src_image,
  *     invalid.
  * @retval ::WIMLIB_ERR_INVALID_PARAM
  *     @a target was @c NULL, or both ::WIMLIB_EXTRACT_FLAG_HARDLINK and
- *     ::WIMLIB_EXTRACT_FLAG_SYMLINK were specified in @a extract_flags, or
+ *     ::WIMLIB_EXTRACT_FLAG_SYMLINK were specified in @a extract_flags; or
  *     both ::WIMLIB_EXTRACT_FLAG_NTFS and either
  *     ::WIMLIB_EXTRACT_FLAG_HARDLINK or ::WIMLIB_EXTRACT_FLAG_SYMLINK were
- *     specified in @a extract_flags, or ::WIMLIB_EXTRACT_FLAG_NTFS was
- *     specified in @a extract_flags and @a image was ::WIMLIB_ALL_IMAGES.
+ *     specified in @a extract_flags; or ::WIMLIB_EXTRACT_FLAG_NTFS was
+ *     specified in @a extract_flags and @a image was ::WIMLIB_ALL_IMAGES; or
+ *     both ::WIMLIB_EXTRACT_FLAG_NTFS and ::WIMLIB_EXTRACT_FLAG_UNIX_DATA were
+ *     specified in @a extract_flag.
  * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH
  *     The SHA1 message digest of an extracted stream did not match the SHA1
  *     message digest given in the WIM file.
@@ -1293,8 +1327,8 @@ extern int wimlib_get_part_number(const WIMStruct *wim, int *total_parts_ret);
  * @retval ::WIMLIB_ERR_NOMEM
  *     Could not allocate memory.
  * @retval ::WIMLIB_ERR_ICONV_NOT_AVAILABLE
- *     wimlib was configured --without-libntfs-3g at compilation time, and at
- *     runtime the iconv() set of functions did not seem to be available,
+ *     wimlib was configured @c --without-libntfs-3g at compilation time, and
+ *     at runtime the @c iconv() set of functions did not seem to be available,
  *     perhaps due to missing files in the C library installation.
  *
  * If this function is not called or returns nonzero, then it will not be safe
@@ -1385,9 +1419,8 @@ extern int wimlib_join(const char **swms, unsigned num_swms,
 /**
  * Mounts an image in a WIM file on a directory read-only or read-write.
  *
- * The calling thread will be daemonized to service the filesystem, and this
- * function will not return until the image is unmounted, unless an error occurs
- * before the filesystem is successfully mounted.
+ * Unless ::WIMLIB_MOUNT_FLAG_DEBUG is specified or an early error occurs, the
+ * process shall be daemonized.
  *
  * If the mount is read-write (::WIMLIB_MOUNT_FLAG_READWRITE specified),
  * modifications to the WIM are staged in a temporary directory.
@@ -1457,7 +1490,7 @@ extern int wimlib_join(const char **swms, unsigned num_swms,
  * @retval ::WIMLIB_ERR_INVALID_PARAM
  *     @a image is shared among multiple ::WIMStruct's as a result of a call to
  *     wimlib_export_image(), or @a image has been added with
- *     wimlib_add_image() or wimlib_add_image_from_ntfs_volume().
+ *     wimlib_add_image().
  * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE
  *     The metadata resource for @a image in @a wim is invalid.
  * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA
@@ -2077,9 +2110,9 @@ extern int wimlib_unmount_image(const char *dir, int unmount_flags,
  *     @a image does not specify a single existing image in @a wim, and is not
  *     ::WIMLIB_ALL_IMAGES.
  * @retval ::WIMLIB_ERR_INVALID_RESOURCE_HASH
- *     A file that had previously been scanned for inclusion in the WIM by the
- *     wimlib_add_image() or wimlib_add_image_from_ntfs_volume() functions was
- *     concurrently modified, so it failed the SHA1 message digest check.
+ *     A file that had previously been scanned for inclusion in the WIM by
+ *     wimlib_add_image() was concurrently modified, so it failed the SHA1
+ *     message digest check.
  * @retval ::WIMLIB_ERR_INVALID_PARAM
  *     @a path was @c NULL.
  * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE
index 6e04673..088e08b 100755 (executable)
@@ -209,12 +209,12 @@ do_test() {
        tar cf ../test.tar .
        cd ..
 
-       if ! imagex mountrw test.wim tmp.mnt; then
+       if ! imagex mountrw test.wim tmp.mnt --unix-data; then
                error "Failed to mount WIM read-write"
        fi
 
        cd tmp.mnt
-       if ! tar xf ../test.tar --no-same-owner; then
+       if ! tar xf ../test.tar; then
                error "Failed to untar archive on read-write mounted WIM"
        fi
        cd ..
@@ -229,7 +229,7 @@ do_test() {
        fi
 
        cd tmp.mnt
-       if ! tar xf ../test.tar --no-same-owner; then
+       if ! tar xf ../test.tar; then
                error "Failed to untar archive on read-write mounted WIM"
        fi
        cd ..