Implement user-specified staging directory
authorEric Biggers <ebiggers3@gmail.com>
Thu, 22 Nov 2012 18:53:43 +0000 (12:53 -0600)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 22 Nov 2012 18:53:43 +0000 (12:53 -0600)
doc/imagex-mount.1.in
programs/imagex.c
src/mount.c
src/wimlib.h

index 3bfa264..5109867 100644 (file)
@@ -7,7 +7,7 @@ imagex-mount, imagex-mountrw, imagex-unmount \- Mount and unmount an image from
 [--streams-interface=\fIINTERFACE\fR] [--ref="\fIGLOB\fR"]
 .br
 \fBimagex mountrw\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [--check]
-[--streams-interface=\fIINTERFACE\fR]
+[--streams-interface=\fIINTERFACE\fR] [--staging-dir=\fIDIR\fR]
 .br
 \fBimagex unmount\fR \fIDIRECTORY\fR [--commit] [--check]
 
@@ -66,6 +66,9 @@ imagex mount mywim.swm 1 dir --ref="mywim*.swm"
 If wimlib was configured using the --without-fuse 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
+you can only mount one image at a time from a WIM read-write.
+
 All files in the mounted WIM will be accessible regardless of whether there is a
 security descriptor in the WIM associated with the file or not.  New files or
 directories created in a read-write mounted WIM will be created with no security
@@ -75,12 +78,12 @@ to set or get DOS names, file attributes, or security
 descriptors in a mounted WIM.
 
 .SH MOUNT OPTIONS
+
 .TP
 \fB--check\fR
 When reading the WIM, verify its integrity if it contains an integrity table.
 .TP
 \fB--streams-interface\fR=\fIINTERFACE\fR
-
 This option is inspired by the ntfs-3g filesystem driver (see \fBntfs-3g\fR
 (8)).  It controls how alternate data streams, or named data streams, in WIM
 files are made available.
@@ -101,19 +104,22 @@ Please note that named data streams are a somewhat obscure NTFS feature that
 aren't actually used much, even though they complicate the WIM file format
 considerably.  Normally, all you care about is the default or "unnamed" data
 stream.
-
 .TP
 \fB--debug\fR
 Turn on debugging information printed by the FUSE library, and do not fork into
 the background.
-
 .TP
 \fB--ref\fR="\fIGLOB\fR"
 File glob of additional split WIM parts that are part of the split WIM being
 mounted.  This option is valid for \fBimagex mount\fR but not \fBimagex
 mountrw\fR.  See \fBSPLIT_WIMS\fR.
+.TP
+\fB--staging-dir\fR=\fIDIR\fR
+Store temporary staging files in the directory \fIDIR\fR.  Only valid for
+\fBimagex mountrw\fR.
 
 .SH UNMOUNT OPTIONS
+
 .TP
 \fB--commit\fR
 Recreate the WIM file with the changes that have been made.  Has no effect if
@@ -126,9 +132,11 @@ mount is read-only or if --commit was not specified.
 .SH IMPLEMENTATION DETAILS
 
 Since a WIM is an archive and not a filesystem, \fBimagex mountrw\fR creates a
-temporary staging directory to contain files that are created or modified.  When
-the filesystem is unmounted with \fB--commit\fR, the WIM is rebuilt, merging in
-the staging files as needed.  Then, the temporary staging directory is deleted.
+temporary staging directory to contain files that are created or modified.  This
+directory is located in the same directory as \fIWIMFILE\fR by default, but the
+location can be set using the \fB--staging-dir\fR option.  When the filesystem
+is unmounted with \fB--commit\fR, the WIM is rebuilt, merging in the staging
+files as needed.  Then, the temporary staging directory is deleted.
 
 \fBimagex unmount\fR executes the \fBfusermount\fR (1) program, which should be
 installed as part of libfuse, to unmount the filesystem.  It then uses a POSIX
index 2547b07..f275782 100644 (file)
@@ -98,7 +98,8 @@ static const char *usage_strings[] = {
 "                    [--ref=\"GLOB\"]\n",
 [MOUNTRW] =
 "imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n"
-"                      [--check] [--debug] [--streams-interface=INTERFACE]\n",
+"                      [--check] [--debug] [--streams-interface=INTERFACE]\n"
+"                      [--staging-dir=DIR]\n",
 [OPTIMIZE] =
 "imagex optimize WIMFILE [--check] [--recompress]\n",
 [SPLIT] =
@@ -166,10 +167,11 @@ static const struct option join_options[] = {
 };
 
 static const struct option mount_options[] = {
-       {"check", no_argument, NULL, 'c'},
-       {"debug", no_argument, NULL, 'd'},
+       {"check",             no_argument,       NULL, 'c'},
+       {"debug",             no_argument,       NULL, 'd'},
        {"streams-interface", required_argument, NULL, 's'},
-       {"ref",      required_argument, NULL, 'r'},
+       {"ref",               required_argument, NULL, 'r'},
+       {"staging-dir",       required_argument, NULL, 'D'},
        {NULL, 0, NULL, 0},
 };
 
@@ -1386,6 +1388,7 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
        const char *swm_glob = NULL;
        WIMStruct **additional_swms = NULL;
        unsigned num_additional_swms = 0;
+       const char *staging_dir = NULL;
 
        if (strcmp(argv[0], "mountrw") == 0)
                mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE;
@@ -1413,6 +1416,9 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
                case 'r':
                        swm_glob = optarg;
                        break;
+               case 'D':
+                       staging_dir = optarg;
+                       break;
                default:
                        goto mount_usage;
                }
@@ -1464,7 +1470,7 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv)
        }
 
        ret = wimlib_mount_image(w, image, dir, mount_flags, additional_swms,
-                                num_additional_swms, NULL);
+                                num_additional_swms, staging_dir);
        if (ret != 0) {
                imagex_error("Failed to mount image %d from `%s' on `%s'",
                             image, wimfile, dir);
index 1290bd5..b0d256e 100644 (file)
@@ -65,9 +65,6 @@ struct wimfs_context {
        /* The WIMStruct for the mounted WIM. */
        WIMStruct *wim;
 
-       /* Working directory when `imagex mount' is run. */
-       char *working_directory;
-
        /* Name of the staging directory for a read-write mount.  Whenever a new file is
         * created, it is done so in the staging directory.  Furthermore, whenever a
         * file in the WIM is modified, it is extracted to the staging directory.  If
@@ -507,37 +504,70 @@ out_delete_staging_file:
  * Creates a randomly named staging directory and saves its name in the
  * filesystem context structure.
  */
-static int make_staging_dir(struct wimfs_context *ctx)
+static int make_staging_dir(struct wimfs_context *ctx,
+                           const char *user_prefix)
 {
-       /* XXX Give the user an option of where to stage files */
-
-       static const char prefix[] = "wimlib-staging-";
-       static const size_t prefix_len = sizeof(prefix) - 1;
        static const size_t random_suffix_len = 10;
+       static const char *common_suffix = ".staging";
+       static const size_t common_suffix_len = 8;
 
-       size_t pwd_len = strlen(ctx->working_directory);
+       char *staging_dir_name = NULL;
+       size_t staging_dir_name_len;
+       size_t prefix_len;
+       const char *wim_basename;
+       char *real_user_prefix = NULL;
+       int ret;
 
-       ctx->staging_dir_name_len = pwd_len + 1 + prefix_len + random_suffix_len;
+       if (user_prefix) {
+               real_user_prefix = realpath(user_prefix, NULL);
+               if (!real_user_prefix) {
+                       ERROR_WITH_ERRNO("Could not resolve `%s'",
+                                        real_user_prefix);
+                       ret = WIMLIB_ERR_NOTDIR;
+                       goto out;
+               }
+               wim_basename = path_basename(ctx->wim->filename);
+               prefix_len = strlen(real_user_prefix) + 1 + strlen(wim_basename);
+       } else {
+               prefix_len = strlen(ctx->wim->filename);
+       }
 
-       ctx->staging_dir_name = MALLOC(ctx->staging_dir_name_len + 1);
-       if (!ctx->staging_dir_name)
-               return WIMLIB_ERR_NOMEM;
+       staging_dir_name_len = prefix_len + common_suffix_len + random_suffix_len;
+
+       staging_dir_name = MALLOC(staging_dir_name_len + 1);
+       if (!staging_dir_name) {
+               ret = WIMLIB_ERR_NOMEM;
+               goto out;
+       }
+
+       if (real_user_prefix)
+               sprintf(staging_dir_name, "%s/%s", real_user_prefix, wim_basename);
+       else
+               strcpy(staging_dir_name, ctx->wim->filename);
+
+       strcat(staging_dir_name, common_suffix);
+
+       randomize_char_array_with_alnum(staging_dir_name + prefix_len + common_suffix_len,
+                                       random_suffix_len);
 
-       memcpy(ctx->staging_dir_name, ctx->working_directory, pwd_len);
-       ctx->staging_dir_name[pwd_len] = '/';
-       memcpy(ctx->staging_dir_name + pwd_len + 1, prefix, prefix_len);
-       randomize_char_array_with_alnum(ctx->staging_dir_name + pwd_len +
-                                       1 + prefix_len, random_suffix_len);
-       ctx->staging_dir_name[ctx->staging_dir_name_len] = '\0';
+       staging_dir_name[staging_dir_name_len] = '\0';
 
-       if (mkdir(ctx->staging_dir_name, 0700) != 0) {
+       if (mkdir(staging_dir_name, 0700) != 0) {
                ERROR_WITH_ERRNO("Failed to create temporary directory `%s'",
-                                ctx->staging_dir_name);
-               FREE(ctx->staging_dir_name);
-               ctx->staging_dir_name = NULL;
-               return WIMLIB_ERR_MKDIR;
+                                staging_dir_name);
+               ret = WIMLIB_ERR_MKDIR;
+       } else {
+               ret = 0;
        }
-       return 0;
+out:
+       FREE(real_user_prefix);
+       if (ret == 0) {
+               ctx->staging_dir_name = staging_dir_name;
+               ctx->staging_dir_name_len = staging_dir_name_len;
+       } else {
+               FREE(staging_dir_name);
+       }
+       return ret;
 }
 
 static int remove_file_or_directory(const char *fpath, const struct stat *sb,
@@ -873,7 +903,7 @@ static int inode_close_fds(struct inode *inode)
 }
 
 /* Overwrites the WIM file, with changes saved. */
-static int rebuild_wim(struct wimfs_context *ctx, bool check_integrity)
+static int rebuild_wim(struct wimfs_context *ctx, int write_flags)
 {
        int ret;
        struct lookup_table_entry *lte, *tmp;
@@ -896,7 +926,7 @@ static int rebuild_wim(struct wimfs_context *ctx, bool check_integrity)
 
        xml_update_image_info(w, w->current_image);
 
-       ret = wimlib_overwrite(w, check_integrity, 0, NULL);
+       ret = wimlib_overwrite(w, write_flags, 0, NULL);
        if (ret != 0) {
                ERROR("Failed to commit changes");
                return ret;
@@ -923,6 +953,7 @@ static void wimfs_destroy(void *p)
        char status;
        char *mailbox;
        struct wimfs_context *ctx = wimfs_get_context();
+       int write_flags;
 
        if (open_message_queues(ctx, true))
                return;
@@ -953,13 +984,11 @@ static void wimfs_destroy(void *p)
        status = 0;
        if (ctx->mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) {
                if (commit) {
-                       ret = chdir(ctx->working_directory);
-                       if (ret == 0) {
-                               status = rebuild_wim(ctx, (check_integrity != 0));
-                       } else {
-                               ERROR_WITH_ERRNO("chdir()");
-                               status = WIMLIB_ERR_NOTDIR;
-                       }
+                       if (check_integrity)
+                               write_flags = WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
+                       else
+                               write_flags = 0;
+                       status = rebuild_wim(ctx, write_flags);
                }
                ret = delete_staging_dir(ctx);
                if (ret != 0) {
@@ -1929,19 +1958,13 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
 
        DEBUG("Initializing struct wimfs_context");
        init_wimfs_context(&ctx);
-
-       DEBUG("Getting current directory");
-       ctx.working_directory = getcwd(NULL, 0);
-       if (!ctx.working_directory) {
-               ERROR_WITH_ERRNO("Could not determine current directory");
-               ret = WIMLIB_ERR_NOTDIR;
-               goto out;
-       }
+       ctx.wim = wim;
+       ctx.mount_flags = mount_flags;
 
        DEBUG("Unlinking message queues in case they already exist");
        ret = set_message_queue_names(&ctx, dir);
        if (ret != 0)
-               goto out_free_working_directory;
+               goto out;
        unlink_message_queues(&ctx);
 
        DEBUG("Preparing arguments to fuse_main()");
@@ -1971,7 +1994,7 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        argv[argc++] = optstring;
        if ((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE)) {
                /* Read-write mount.  Make the staging directory */
-               ret = make_staging_dir(&ctx);
+               ret = make_staging_dir(&ctx, staging_dir);
                if (ret != 0)
                        goto out_free_dir_copy;
        } else {
@@ -2007,9 +2030,6 @@ WIMLIBAPI int wimlib_mount_image(WIMStruct *wim, int image, const char *dir,
        ctx.next_ino = assign_inode_numbers(&imd->inode_list);
        DEBUG("(next_ino = %"PRIu64")", ctx.next_ino);
 
-       /* Finish initializing the filesystem context. */
-       ctx.wim = wim;
-       ctx.mount_flags = mount_flags;
 
        DEBUG("Calling fuse_main()");
 
@@ -2022,9 +2042,6 @@ out_free_dir_copy:
        FREE(dir_copy);
 out_free_message_queue_names:
        free_message_queue_names(&ctx);
-out_free_working_directory:
-       FREE(ctx.working_directory);
-       ctx.working_directory = NULL;
 out:
        if (num_additional_swms) {
                free_lookup_table(wim->lookup_table);
index 0ba59d4..08fb9a0 100644 (file)
@@ -1395,8 +1395,10 @@ extern int wimlib_join(const char **swms, unsigned num_swms,
  *     This number should be one less than the total number of parts in the
  *     split WIM.  Set to 0 if the WIM is a standalone WIM.
  * @param staging_dir
- *     Currently ignored, but may provide a way to specify the staging
- *     directory in the future.  Set to @c NULL.
+ *     If non-NULL, the name of a directory in which the staging directory will
+ *     be created.  Ignored if ::WIMLIB_MOUNT_FLAG_READWRITE is not specified
+ *     in @a mount_flags.  If left @c NULL, the staging directory is created in
+ *     the same directory as the WIM file that @a wim was originally read from.
  *
  * @return 0 on success; nonzero on error.
  * @retval ::WIMLIB_ERR_ALREADY_LOCKED