From 512d3f87a1e7b59ca19ae6d6965dbcf7f4a17c15 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 31 Aug 2012 20:31:02 -0500 Subject: [PATCH 01/16] Split WIM mount and split WIM documentation --- README | 2 + doc/imagex-apply.1.in | 41 +++++++- doc/imagex-mount.1.in | 49 ++++++++- doc/imagex.1.in | 70 +++++++------ programs/imagex.c | 233 +++++++++++++++++++++++++++--------------- src/join.c | 3 +- src/mount.c | 66 ++++++++++-- src/wimlib.h | 26 ++++- 8 files changed, 356 insertions(+), 134 deletions(-) diff --git a/README b/README index c0dadaad..91deb1c0 100644 --- a/README +++ b/README @@ -25,6 +25,8 @@ format, and LZX compression format. The XPRESS documentation is acceptable, but the LZX documentation is not entirely correct, and the WIM documentation itself is very incomplete and is of unacceptable quality. +A WIM file may be either stand-alone or split into multiple parts. + WINDOWS PE A major use for this library is to create customized images of Windows PE, the diff --git a/doc/imagex-apply.1.in b/doc/imagex-apply.1.in index 06caf05e..e9dd4684 100644 --- a/doc/imagex-apply.1.in +++ b/doc/imagex-apply.1.in @@ -23,6 +23,9 @@ location and the WIM image(s) are extracted to that directory. If \fITARGET\fR specifies a regular file or block device, it is interpreted as a NTFS volume to which the WIM image is to be extracted. +\fBimagex apply\fR supports applying images from stand-alone WIMs as well as +split WIM. See \fBSPLIT WIMS\fR. + .SH NORMAL MODE The normal extraction mode is entered when \fITARGET\fR is a directory or @@ -155,6 +158,38 @@ Besides setting up the files on the "System" partition, don't forget to set the bootable flag on it, and have a master boot record that loads the bootable partition (Windows' MBR does, and SYSLINUX provides an equivalent MBR). +.SH SPLIT WIMS + +You may use \fBimagex apply\fR to apply images from a split WIM. The +\fIWIMFILE\fR argument is used to specify the first part of the split WIM, and +the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob +that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to +be a single string on the command line, so \fIGLOB\fR must be quoted so that it +is protected against shell expansion. \fIGLOB\fR must expand to all parts of +the split WIM, except optionally the first part which may either omitted or +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 +mywim.swm +mywim2.swm +mywim3.swm +mywim4.swm +mywim5.swm +\. ... etc. +.RE + +To apply the first image of this split WIM to the directory "dir", we would do: +.PP +.RS +imagex apply mywim.swm 1 dir --ref="mywim*.swm" +.RE +.PP + .SH OPTIONS .TP 6 \fB--check\fR @@ -177,11 +212,13 @@ instead. This option is not available in the NTFS extraction mode. \fB--verbose\fR Print the path to of each file or directory within the WIM image as it is extracted, and some additional informational messages. +.TP +\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. .SH NOTES -\fBimagex apply\fR does not yet support split WIMs. - \fBimagex apply\fR calculates the SHA1 message digest of every file stream it extracts and verifies that it is the same as the SHA1 message digest provided in the WIM file. It is an error if the message digests don't match. It's also diff --git a/doc/imagex-mount.1.in b/doc/imagex-mount.1.in index 3f5eb4a6..2518f350 100644 --- a/doc/imagex-mount.1.in +++ b/doc/imagex-mount.1.in @@ -4,7 +4,7 @@ imagex mount, mountrw, 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] +[--streams-interface=\fIINTERFACE\fR] [--ref="\fIGLOB\fR"] .br \fBimagex mountrw\fR \fIWIMFILE\fR \fIIMAGE\fR \fIDIRECTORY\fR [--check] [--streams-interface=\fIINTERFACE\fR] @@ -28,6 +28,40 @@ The WIM image can be unmounted using the \fBimagex unmount\fR command. Changes made to a WIM mounted read-write will be discarded unless the \fB--commit\fR flag is provided to \fBimagex unmount\fR. +.SH SPLIT WIMS + +You may use \fBimagex mount\fR to mount an image from a split WIM read-only. +However, you may not mount an image from a split WIM read-write. + +The \fIWIMFILE\fR argument is used to specify the first part of the split WIM, and +the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob +that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to +be a single string on the command line, so \fIGLOB\fR must be quoted so that it +is protected against shell expansion. \fIGLOB\fR must expand to all parts of +the split WIM, except optionally the first part which may either omitted or +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 +mywim.swm +mywim2.swm +mywim3.swm +mywim4.swm +mywim5.swm +\. ... etc. +.RE + +To mount the first image of this split WIM to the directory "dir", we would do: +.PP +.RS +imagex mount mywim.swm 1 dir --ref="mywim*.swm" +.RE +.PP + .SH NOTES If wimlib was configured using the --without-fuse flag, then the \fBimagex @@ -36,9 +70,10 @@ mount\fR, \fBimagex mountrw\fR, and \fBimagex unmount\fR commands will not work. 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 -descriptor. - -Mounting split WIMs is not yet supported. +descriptor. Although there is support for accessing named data streams (see the +\fB--streams-interface\fR option), it is currently not possible +to set or get DOS names, file attributes, or security +descriptors in a mounted WIM. .SH MOUNT OPTIONS .TP @@ -73,6 +108,12 @@ stream. 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. + .SH UNMOUNT OPTIONS .TP \fB--commit\fR diff --git a/doc/imagex.1.in b/doc/imagex.1.in index 1352ce77..263b2c96 100644 --- a/doc/imagex.1.in +++ b/doc/imagex.1.in @@ -44,46 +44,52 @@ There is a separate manual page for each \fBimagex\fR command. The following features are currently supported: -.IP \[bu] 2 -Mount an image in a WIM read-only (\fBimagex mount\fR) -.IP \[bu] 2 -Mount an image in a WIM read-write (\fBimagex mountrw\fR) -.IP \[bu] 2 -Create a WIM from a directory or NTFS volume (\fBimagex capture\fR) -.IP \[bu] 2 -Append a directory or NTFS volume onto a WIM as a new image (\fBimagex +.IP \[bu] 3 +Create a stand-alone WIM from a directory or NTFS volume (\fBimagex capture\fR) +.IP \[bu] +Append a directory or NTFS volume onto a stand-alone WIM as a new image (\fBimagex append\fR) -.IP \[bu] 2 -Delete image(s) from a WIM (\fBimagex delete\fR) -.IP \[bu] 2 -Export image(s) from a WIM (\fBimagex export\fR) -.IP \[bu] 2 +.IP \[bu] +Apply an image from a stand-alone or split WIM to a directory or NTFS volume +(\fBimagex apply\fR) +.IP \[bu] +Mount an image from a stand-alone or split WIM read-only (\fBimagex mount\fR) +.IP \[bu] +Mount an image from a stand-alone WIM read-write (\fBimagex mountrw\fR) +.IP \[bu] +Delete image(s) from a stand-alone WIM (\fBimagex delete\fR) +.IP \[bu] +Export image(s) from a stand-alone WIM (\fBimagex export\fR) +.IP \[bu] Display information about a WIM file (\fBimagex info\fR, \fBimagex dir\fR) -.IP \[bu] 2 +.IP \[bu] Change the name or description of an image in the WIM (\fBimagex info\fR) -.IP \[bu] 2 +.IP \[bu] Change which image in a WIM is bootable (\fBimagex info\fR) -.IP \[bu] 2 -Combine split WIMs into one WIM (\fBimage join\fR) -.IP \[bu] 2 -Split a WIM into multiple parts (\fBimage split\fR) -.IP \[bu] 2 +.IP \[bu] +Combine split WIMs into one stand-alone WIM (\fBimage join\fR) +.IP \[bu] +Split a stand-alone WIM into multiple parts (\fBimage split\fR) +.IP \[bu] Support for all WIM compression types, both compression and decompression (LZX, XPRESS, and none) -.IP \[bu] 2 -Integrity table -.IP \[bu] 2 -XML data (parsed and written using \fBlibxml\fR(3)) +.IP \[bu] +WIM integrity table is supported (\fB--check\fR option to many commands) +.IP \[bu] +WIM XML data (parsed and written using \fBlibxml\fR(3)) .SH UNSUPPORTED FEATURES As of version 1.0.0, wimlib supports capturing and applying WIMs directly from -NTFS and has much improved support for hard links and symbolic links. I don't -think there are many other features that would be worth it to implement; the -only significant thing missing (in my opinion) is that split WIMs need to be -handled better (e.g. it should be possible to apply a split WIM using \fBimagex -apply\fR). And if Microsoft updates the WIM format, I'd need to support it, but -it looks like the format for Windows 8 is the same as that of Windows 7. +NTFS and has much improved support for hard links and symbolic links. In +addition, you may now apply split WIMs and mount them read-only. I don't think +there are many other features that would be worth it to implement. Besides +porting the library to Windows (which I'm not really interested in), the main +thing that could use improvement (in my opinion) is that the LZX compression +ratio still isn't quite as good as Microsoft's version. Also, exporting an +image from a split WIM could be supported. Furthermore, if Microsoft updates +the WIM format, I'd need to support it, but it looks like the format for Windows +8 is the same as that of Windows 7. .SH DIFFERENCES FROM MICROSOFT IMAGEX @@ -101,7 +107,9 @@ Some features, such as the ability to keep files hard-linked across WIM images when they are extracted from a WIM, are not available in Microsoft's version of imagex. Also, doesn't seem to be an equivalent of \fBimagex join\fR in Microsoft's version; you would have to use \fBimagex.exe /export\fR, but that -doesn't let you export all images at once. +doesn't let you export all images at once. Furthermore, this version of +\fBimagex\fR lets you mount an image from a split WIM read-only, while +Microsoft's version does not. Microsoft's version has some weird limitations, like it won't let you extract a WIM on a shared folder, and it requires some commands to be run only from diff --git a/programs/imagex.c b/programs/imagex.c index 12bfbc62..07381a9b 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -56,23 +56,8 @@ enum imagex_op_type { UNMOUNT, }; -static const char *path_basename(const char *path) -{ - const char *p = path; - while (*p) - p++; - p--; - - /* Trailing slashes. */ - while ((p != path - 1) && *p == '/') - p--; - - while ((p != path - 1) && *p != '/') - p--; - - return p + 1; -} - +static void usage(int cmd_type); +static void usage_all(); static const char *usage_strings[] = { [APPEND] = @@ -82,7 +67,7 @@ static const char *usage_strings[] = { [APPLY] = " imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n" " (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n" -" [--symlink] [--verbose]\n", +" [--symlink] [--verbose] [--ref=\"GLOB\"]\n", [CAPTURE] = " imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [IMAGE_NAME]\n" " [DESCRIPTION] [--boot] [--check] [--compress=TYPE]\n" @@ -105,7 +90,8 @@ static const char *usage_strings[] = { " imagex join [--check] WIMFILE SPLIT_WIM...\n", [MOUNT] = " imagex mount WIMFILE (IMAGE_NUM | IMAGE_NAME) DIRECTORY\n" -" [--check] [--debug] [--streams-interface=INTERFACE]\n", +" [--check] [--debug] [--streams-interface=INTERFACE]\n" +" [--ref=\"GLOB\"]\n", [MOUNTRW] = " imagex mountrw WIMFILE [IMAGE_NUM | IMAGE_NAME] DIRECTORY\n" " [--check] [--debug] [--streams-interface=INTERFACE]\n", @@ -180,6 +166,7 @@ static const struct option mount_options[] = { {"check", no_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, {"streams-interface", required_argument, NULL, 's'}, + {"ref", required_argument, NULL, 'r'}, {NULL, 0, NULL, 0}, }; @@ -195,6 +182,7 @@ static const struct option unmount_options[] = { }; + /* Print formatted error message to stderr. */ static void imagex_error(const char *format, ...) { @@ -218,40 +206,24 @@ static void imagex_error_with_errno(const char *format, ...) va_end(va); } - -static inline void version() +static const char *path_basename(const char *path) { - static const char *s = - "imagex (" PACKAGE ") " PACKAGE_VERSION "\n" - "Copyright (C) 2012 Eric Biggers\n" - "License GPLv3+; GNU GPL version 3 or later .\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n" - "\n" - "Report bugs to "PACKAGE_BUGREPORT".\n"; - fputs(s, stdout); -} + const char *p = path; + while (*p) + p++; + p--; -static inline void usage(int cmd) -{ - puts("IMAGEX: Usage:"); - fputs(usage_strings[cmd], stdout); -} + /* Trailing slashes. */ + while ((p != path - 1) && *p == '/') + p--; -static void usage_all() -{ - puts("IMAGEX: Usage:"); - for (int i = 0; i < ARRAY_LEN(usage_strings); i++) - fputs(usage_strings[i], stdout); - static const char *extra = -" imagex --help\n" -" imagex --version\n" -"\n" -" The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n" - ; - fputs(extra, stdout); + while ((p != path - 1) && *p != '/') + p--; + + return p + 1; } + static int verify_image_exists(int image) { if (image == WIM_NO_IMAGE) { @@ -333,6 +305,62 @@ out_fclose: return NULL; } +static int open_swms_from_glob(const char *swm_glob, + const char *first_part, + int open_flags, + WIMStruct ***additional_swms_ret, + unsigned *num_additional_swms_ret) +{ + unsigned num_additional_swms = 0; + WIMStruct **additional_swms = NULL; + glob_t globbuf; + int ret; + + ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + if (ret != 0) { + if (ret == GLOB_NOMATCH) { + imagex_error("Found no files for glob \"%s\"", + swm_glob); + } else { + imagex_error_with_errno("Failed to process glob " + "\"%s\"", swm_glob); + } + ret = -1; + goto out; + } + num_additional_swms = globbuf.gl_pathc; + additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0])); + if (!additional_swms) { + imagex_error("Out of memory"); + ret = -1; + goto out_globfree; + } + size_t offset = 0; + for (size_t i = 0; i < num_additional_swms; i++) { + if (strcmp(globbuf.gl_pathv[i], first_part) == 0) { + offset++; + continue; + } + ret = wimlib_open_wim(globbuf.gl_pathv[i], + open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, + &additional_swms[i - offset]); + if (ret != 0) + goto out_close_swms; + } + *additional_swms_ret = additional_swms; + *num_additional_swms_ret = num_additional_swms - offset; + ret = 0; + goto out_globfree; +out_close_swms: + for (unsigned i = 0; i < num_additional_swms; i++) + wimlib_free(additional_swms[i]); + free(additional_swms); +out_globfree: + globfree(&globbuf); +out: + return ret; +} + static int imagex_append(int argc, const char **argv) { int c; @@ -459,8 +487,7 @@ static int imagex_apply(int argc, const char **argv) const char *swm_glob = NULL; WIMStruct **additional_swms = NULL; - size_t num_additional_swms = 0; - glob_t globbuf; + unsigned num_additional_swms = 0; for_opt(c, apply_options) { switch (c) { @@ -519,33 +546,11 @@ static int imagex_apply(int argc, const char **argv) } if (swm_glob) { - ret = glob(swm_glob, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); - if (ret != 0) { - imagex_error_with_errno("Failed to process glob " - "\"%s\"", swm_glob); - ret = -1; - goto out; - } - num_additional_swms = globbuf.gl_pathc; - additional_swms = calloc(num_additional_swms, sizeof(additional_swms[0])); - if (!additional_swms) { - imagex_error("Out of memory"); - ret = -1; + ret = open_swms_from_glob(swm_glob, wimfile, open_flags, + &additional_swms, + &num_additional_swms); + if (ret != 0) goto out; - } - size_t offset = 0; - for (size_t i = 0; i < num_additional_swms; i++) { - if (strcmp(globbuf.gl_pathv[i], wimfile) == 0) { - offset++; - continue; - } - ret = wimlib_open_wim(globbuf.gl_pathv[i], - open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, - &additional_swms[i - offset]); - if (ret != 0) - goto out; - } - num_additional_swms -= offset; } #ifdef WITH_NTFS_3G @@ -1242,13 +1247,17 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) { int c; int mount_flags = 0; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; + int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS | + WIMLIB_OPEN_FLAG_SPLIT_OK; const char *wimfile; const char *dir; WIMStruct *w; int image; int num_images; int ret; + const char *swm_glob = NULL; + WIMStruct **additional_swms = NULL; + unsigned num_additional_swms = 0; if (strcmp(argv[0], "mountrw") == 0) mount_flags |= WIMLIB_MOUNT_FLAG_READWRITE; @@ -1272,6 +1281,9 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) goto mount_usage; } break; + case 'r': + swm_glob = optarg; + break; default: goto mount_usage; } @@ -1287,6 +1299,14 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) if (ret != 0) return ret; + if (swm_glob) { + ret = open_swms_from_glob(swm_glob, wimfile, open_flags, + &additional_swms, + &num_additional_swms); + if (ret != 0) + goto out; + } + if (argc == 2) { image = 1; num_images = wimlib_get_num_images(w); @@ -1296,7 +1316,7 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) ? MOUNTRW : MOUNT); ret = WIMLIB_ERR_INVALID_IMAGE; - goto done; + goto out; } dir = argv[1]; } else { @@ -1306,16 +1326,20 @@ static int imagex_mount_rw_or_ro(int argc, const char **argv) ret = verify_image_exists_and_is_single(image); if (ret != 0) - goto done; + goto out; - ret = wimlib_mount(w, image, dir, mount_flags); + ret = wimlib_mount(w, image, dir, mount_flags, additional_swms, + num_additional_swms); if (ret != 0) { imagex_error("Failed to mount image %d from `%s' on `%s'", image, wimfile, dir); } -done: +out: wimlib_free(w); + if (additional_swms) + for (unsigned i = 0; i < num_additional_swms; i++) + wimlib_free(additional_swms[i]); return ret; mount_usage: usage((mount_flags & WIMLIB_MOUNT_FLAG_READWRITE) @@ -1390,6 +1414,10 @@ struct imagex_command { int cmd; }; + +#define for_imagex_command(p) for (p = &imagex_commands[0]; \ + p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++) + static struct imagex_command imagex_commands[] = { {"append", imagex_append, APPEND}, {"apply", imagex_apply, APPLY}, @@ -1405,8 +1433,19 @@ static struct imagex_command imagex_commands[] = { {"unmount", imagex_unmount, UNMOUNT}, }; -#define for_imagex_command(p) for (p = &imagex_commands[0]; \ - p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++) +static void version() +{ + static const char *s = + "imagex (" PACKAGE ") " PACKAGE_VERSION "\n" + "Copyright (C) 2012 Eric Biggers\n" + "License GPLv3+; GNU GPL version 3 or later .\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n" + "Report bugs to "PACKAGE_BUGREPORT".\n"; + fputs(s, stdout); +} + static void help_or_version(int argc, const char **argv) { @@ -1440,6 +1479,34 @@ static void help_or_version(int argc, const char **argv) } +static void usage(int cmd_type) +{ + struct imagex_command *cmd; + puts("IMAGEX: Usage:"); + fputs(usage_strings[cmd_type], stdout); + for_imagex_command(cmd) + if (cmd->cmd == cmd_type) + printf("\nTry `man imagex-%s' for more details.\n", + cmd->name); +} + +static void usage_all() +{ + puts("IMAGEX: Usage:"); + for (int i = 0; i < ARRAY_LEN(usage_strings); i++) + fputs(usage_strings[i], stdout); + static const char *extra = +" imagex --help\n" +" imagex --version\n" +"\n" +" The compression TYPE may be \"maximum\", \"fast\", or \"none\".\n" +"\n" +" Try `man imagex' for more information.\n" + ; + fputs(extra, stdout); +} + + int main(int argc, const char **argv) { struct imagex_command *cmd; diff --git a/src/join.c b/src/join.c index 3710eec8..07cbfa97 100644 --- a/src/join.c +++ b/src/join.c @@ -31,10 +31,11 @@ static int copy_lte_to_table(struct lookup_table_entry *lte, void *table) { struct lookup_table_entry *copy; - copy = new_lookup_table_entry(); + copy = MALLOC(sizeof(struct lookup_table_entry)); if (!copy) return WIMLIB_ERR_NOMEM; memcpy(copy, lte, sizeof(struct lookup_table_entry)); + INIT_LIST_HEAD(©->lte_group_list); lookup_table_insert(table, copy); return 0; } diff --git a/src/mount.c b/src/mount.c index 7d1ba199..6f50a4e5 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1822,35 +1822,54 @@ static int check_lte_refcnt(struct lookup_table_entry *lte, void *ignore) /* Mounts a WIM file. */ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, - int flags) + int flags, WIMStruct **additional_swms, + unsigned num_additional_swms) { int argc = 0; char *argv[16]; int ret; char *p; + struct lookup_table *joined_tab, *wim_tab_save; DEBUG("Mount: wim = %p, image = %d, dir = %s, flags = %d, ", wim, image, dir, flags); - if (!dir) + if (!wim || !dir) return WIMLIB_ERR_INVALID_PARAM; + ret = verify_swm_set(wim, additional_swms, num_additional_swms); + if (ret != 0) + return ret; + + if (num_additional_swms) { + ret = new_joined_lookup_table(wim, additional_swms, + num_additional_swms, + &joined_tab); + if (ret != 0) + return ret; + wim_tab_save = wim->lookup_table; + wim->lookup_table = joined_tab; + } + ret = wimlib_select_image(wim, image); if (ret != 0) - return ret; + goto out; DEBUG("Selected image %d", image); next_link_group_id = assign_link_group_ids(wim->image_metadata[image - 1].lgt); + DEBUG("Resolving lookup table entries"); /* Resolve all the lookup table entries of the dentry tree */ for_dentry_in_tree(wim_root_dentry(wim), dentry_resolve_ltes, wim->lookup_table); + DEBUG("Checking lookup table entry reference counts"); + ret = for_lookup_table_entry(wim->lookup_table, check_lte_refcnt, NULL); if (ret != 0) - return ret; + goto out; if (flags & WIMLIB_MOUNT_FLAG_READWRITE) wim_get_current_image_metadata(wim)->modified = true; @@ -1860,16 +1879,33 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS))) flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR; + DEBUG("Getting current directory"); + mount_dir = dir; working_directory = getcwd(NULL, 0); if (!working_directory) { ERROR_WITH_ERRNO("Could not determine current directory"); - return WIMLIB_ERR_NOTDIR; + ret = WIMLIB_ERR_NOTDIR; + goto out; } + DEBUG("Closing POSIX message queues"); + /* XXX hack to get rid of the message queues if they already exist for + * some reason (maybe left over from a previous mount that wasn't + * unmounted correctly) */ + ret = open_message_queues(true); + if (ret != 0) + goto out; + close_message_queues(); + + DEBUG("Preparing arguments to fuse_main()"); + + p = STRDUP(dir); - if (!p) - return WIMLIB_ERR_NOMEM; + if (!p) { + ret = WIMLIB_ERR_NOMEM; + goto out; + } argv[argc++] = "imagex"; argv[argc++] = p; @@ -1893,7 +1929,8 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, make_staging_dir(); if (!staging_dir_name) { FREE(p); - return WIMLIB_ERR_MKDIR; + ret = WIMLIB_ERR_MKDIR; + goto out; } } else { /* Read-only mount */ @@ -1919,8 +1956,14 @@ WIMLIBAPI int wimlib_mount(WIMStruct *wim, int image, const char *dir, mount_flags = flags; ret = fuse_main(argc, argv, &wimfs_operations, NULL); - - return (ret == 0) ? 0 : WIMLIB_ERR_FUSE; + if (ret) + ret = WIMLIB_ERR_FUSE; +out: + if (num_additional_swms) { + free_lookup_table(wim->lookup_table); + wim->lookup_table = wim_tab_save; + } + return ret; } @@ -2060,7 +2103,8 @@ WIMLIBAPI int wimlib_unmount(const char *dir, int flags) } WIMLIBAPI int wimlib_mount(WIMStruct *wim_p, int image, const char *dir, - int flags) + int flags, WIMStruct **additional_swms, + unsigned num_additional_swms) { return mount_unsupported_error(); } diff --git a/src/wimlib.h b/src/wimlib.h index 0debc5c2..94a0f9d9 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -54,6 +54,8 @@ * but the LZX documentation is not entirely correct, and the WIM documentation * itself is very incomplete and is of unacceptable quality. * + * A WIM file may be either stand-alone or split into multiple parts. + * * \section winpe Windows PE * * A major use for this library is to create customized images of Windows PE, the @@ -612,7 +614,7 @@ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, * @param num_additional_swms * Number of additional WIM parts provided in the @a additional_swms array. * This number should be one less than the total number of parts in the - * split WIM. + * split WIM. Set to 0 if the WIM is a standalone WIM. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -869,6 +871,16 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR, or * ::WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS. The default interface is * the XATTR interface. + * @param additional_swms + * Array of pointers to the ::WIMStruct for each additional part in the + * split WIM. Ignored if @a num_additional_swms is 0. The pointers do not + * need to be in any particular order, but they must include all parts of + * the split WIM other than the first part, which must be provided in the + * @a wim parameter. + * @param num_additional_swms + * Number of additional WIM parts provided in the @a additional_swms array. + * 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. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_DECOMPRESSION @@ -894,8 +906,18 @@ extern int wimlib_join(const char **swms, unsigned num_swms, * @retval ::WIMLIB_ERR_READ * An unexpected end-of-file or read error occurred when trying to read * data from the WIM file associated with @a wim. + * @retval ::WIMLIB_ERR_SPLIT_INVALID + * The WIM is a split WIM, but the parts specified do not form a complete + * split WIM because they do not include all the parts of the original WIM, + * there are duplicate parts, or not all the parts have the same GUID and + * compression type. + * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED + * The WIM is a split WIM and a read-write mount was requested. We only + * support mounting a split WIM read-only. */ -extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags); +extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, + WIMStruct **additional_swms, + unsigned num_additional_swms); /** * Opens a WIM file and creates a ::WIMStruct for it. -- 2.43.0 From 1cfb17b5e36ad9b6148a14a5bcc8a68689ec10ec Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 31 Aug 2012 20:52:07 -0500 Subject: [PATCH 02/16] More comments about flags --- src/wimlib_internal.h | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/wimlib_internal.h b/src/wimlib_internal.h index fa40f6af..b7b2da26 100644 --- a/src/wimlib_internal.h +++ b/src/wimlib_internal.h @@ -71,16 +71,23 @@ struct resource_entry { /* Flags for the `flags' field of the struct resource_entry structure. */ -/* ??? */ +/* I haven't seen this flag used in any of the WIMs I have examined. I assume + * it means that there are no references to the stream, so the space is free. + * However, even after deleting files from a WIM mounted with `imagex.exe + * /mountrw', I could not see this flag being used. Either way, we don't + * actually use this flag for anything. */ #define WIM_RESHDR_FLAG_FREE 0x01 -/* Indicates that a file resource is a metadata resource. */ +/* Indicates that the stream is a metadata resource for a WIM image. */ #define WIM_RESHDR_FLAG_METADATA 0x02 -/* Indicates that a file resource is compressed. */ +/* Indicates that the stream is compressed. */ #define WIM_RESHDR_FLAG_COMPRESSED 0x04 -/* ??? */ +/* I haven't seen this flag used in any of the WIMs I have examined. Perhaps it + * means that a stream could possibly be split among multiple split WIM parts. + * However, `imagex.exe /split' does not seem to create any WIMs like this. + * Either way, we don't actually use this flag for anything. */ #define WIM_RESHDR_FLAG_SPANNED 0x08 @@ -142,8 +149,7 @@ struct wim_header { //u8 unused[WIM_UNUSED_LEN]; }; -/* Flags for the `flags' field of the struct wim_header. */ - +/* Flags for the `flags' field of the struct wim_header: */ /* Reserved for future use by M$ */ #define WIM_HDR_FLAG_RESERVED 0x00000001 @@ -151,26 +157,31 @@ struct wim_header { /* Files and metadata in the WIM are compressed. */ #define WIM_HDR_FLAG_COMPRESSION 0x00000002 -/* WIM is read-only. */ +/* WIM is read-only (we ignore this). */ #define WIM_HDR_FLAG_READONLY 0x00000004 /* Resource data specified by images in this WIM may be contained in a different - * WIM */ + * WIM. Or in other words, this WIM is part of a split WIM. */ #define WIM_HDR_FLAG_SPANNED 0x00000008 -/* The WIM contains resources only; no filesystem metadata. */ +/* The WIM contains resources only; no filesystem metadata. We ignore this + * flag, as we look for file resources in all the WIMs anyway. */ #define WIM_HDR_FLAG_RESOURCE_ONLY 0x00000010 -/* The WIM contains metadata only. */ +/* The WIM contains metadata only. We ignore this flag. Note that all the + * metadata resources for a split WIM should be in the first part. */ #define WIM_HDR_FLAG_METADATA_ONLY 0x00000020 -/* Lock field to prevent multiple writers from writing the WIM concurrently. */ +/* Lock field to prevent multiple writers from writing the WIM concurrently. We + * ignore this flag. */ #define WIM_HDR_FLAG_WRITE_IN_PROGRESS 0x00000040 -/* Reparse point fixup ??? */ +/* Reparse point fixup ??? + * This has something to do with absolute targets of reparse points / symbolic + * links but I don't know what. We ignore this flag. */ #define WIM_HDR_FLAG_RP_FIX 0x00000080 -/* Unknown compression type */ +/* Unused, reserved flag for another compression type */ #define WIM_HDR_FLAG_COMPRESS_RESERVED 0x00010000 /* Resources within the WIM are compressed using "XPRESS" compression, which is @@ -181,7 +192,9 @@ struct wim_header { * a LZ77-based algorithm. */ #define WIM_HDR_FLAG_COMPRESS_LZX 0x00040000 +#ifdef WITH_NTFS_3G typedef struct _ntfs_volume ntfs_volume; +#endif /* Structure for security data. Each image in the WIM file has its own security * data. */ -- 2.43.0 From 6f841e85af6215e88bce12a34a00548664fba6ea Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 31 Aug 2012 22:04:46 -0500 Subject: [PATCH 03/16] Export from split WIM --- doc/imagex-export.1.in | 46 +++++++++++++-- programs/imagex.c | 42 ++++++++++---- src/join.c | 15 +++++ src/lookup_table.h | 126 ++++++++++++++++++++++++++++++----------- src/modify.c | 68 ++++++++++++++-------- src/wimlib.h | 27 +++++++-- 6 files changed, 248 insertions(+), 76 deletions(-) diff --git a/doc/imagex-export.1.in b/doc/imagex-export.1.in index d455e9d1..e80a5d13 100644 --- a/doc/imagex-export.1.in +++ b/doc/imagex-export.1.in @@ -29,6 +29,10 @@ If given, \fIDEST_IMAGE_DESCRIPTION\fR specifies the description to give the image being exported to \fIDEST_WIMFILE\fR. The default is its description in \fISRC_WIMFILE\fR. +\fBimagex export\fR supports exporting images from stand-alone WIMs as well as +from split WIMs. However, you cannot export an image to a split WIM. See +\fBSPLIT WIMS\fR. + .SH OPTIONS .TP 6 \fB--boot\fR @@ -56,16 +60,50 @@ compression type must be the same as that of \fIDEST_WIMFILE\fR. You may also specify the actual names of the compression algorithms, "XPRESS" and "LZX", instead of "fast" and "maximum", respectively. -.SH NOTES +.TP +\fB--ref\fR="\fIGLOB\fR" +File glob of additional split WIM parts that are part of the split WIM being +exported. See \fBSPLIT_WIMS\fR. + +.SH SPLIT WIMS + +You may use \fBimagex export\fR to export images from a split WIM. The +\fISRC_WIMFILE\fR argument is used to specify the first part of the split WIM, and +the \fB--refs\fR="\fIGLOB\fR" option is used to provide a shell-style file glob +that specifies the additional parts of the split WIM. \fIGLOB\fR is expected to +be a single string on the command line, so \fIGLOB\fR must be quoted so that it +is protected against shell expansion. \fIGLOB\fR must expand to all parts of +the split WIM, except optionally the first part which may either omitted or +included in the glob (but the first part MUST be specified as \fISRC_WIMFILE\fR as +well). + +Here's an example. The names for the split WIMs usually go something like: + +.RS +.PP +.nf +mywim.swm +mywim2.swm +mywim3.swm +mywim4.swm +mywim5.swm +\. ... etc. +.RE -\fBimagex export\fR does not yet support split WIMs. +To export the first image of this split WIM to a new or existing WIM file +"other.wim", run: +.PP +.RS +imagex export mywim.swm 1 other.wim --ref="mywim*.swm" +.RE +.PP .SH EXAMPLES -Export the second image of 'boot.wim' to the new WIM file 'image2.wim', and +Export the second image of 'boot.wim' to the new WIM file 'new.wim', and change the compression type to maximum, if it wasn't maximum already: .RS .PP -image export boot.wim 2 image2.wim --compress=maximum +image export boot.wim 2 new.wim --compress=maximum .RE .PP diff --git a/programs/imagex.c b/programs/imagex.c index 07381a9b..fac692f0 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -81,7 +81,7 @@ static const char *usage_strings[] = { " imagex export SRC_WIMFILE (SRC_IMAGE_NUM | SRC_IMAGE_NAME | all ) \n" " DEST_WIMFILE [DEST_IMAGE_NAME]\n" " [DEST_IMAGE_DESCRIPTION] [--boot] [--check]\n" -" [--compress=TYPE]\n", +" [--compress=TYPE] [--ref=\"GLOB\"]\n", [INFO] = " imagex info WIMFILE [IMAGE_NUM | IMAGE_NAME] [NEW_NAME]\n" " [NEW_DESC] [--boot] [--check] [--header] [--lookup-table]\n" @@ -143,6 +143,7 @@ static const struct option export_options[] = { {"boot", no_argument, NULL, 'b'}, {"check", no_argument, NULL, 'c'}, {"compress", required_argument, NULL, 'x'}, + {"ref", required_argument, NULL, 'r'}, {NULL, 0, NULL, 0}, }; @@ -851,6 +852,9 @@ static int imagex_export(int argc, const char **argv) int image; struct stat stbuf; bool wim_is_new; + const char *swm_glob = NULL; + WIMStruct **additional_swms = NULL; + unsigned num_additional_swms = 0; for_opt(c, export_options) { switch (c) { @@ -867,6 +871,9 @@ static int imagex_export(int argc, const char **argv) return -1; compression_type_specified = true; break; + case 'r': + swm_glob = optarg; + break; default: usage(EXPORT); return -1; @@ -883,7 +890,8 @@ static int imagex_export(int argc, const char **argv) dest_wimfile = argv[2]; dest_name = (argc >= 4) ? argv[3] : NULL; dest_desc = (argc >= 5) ? argv[4] : NULL; - ret = wimlib_open_wim(src_wimfile, open_flags, &src_w); + ret = wimlib_open_wim(src_wimfile, + open_flags | WIMLIB_OPEN_FLAG_SPLIT_OK, &src_w); if (ret != 0) return ret; @@ -896,11 +904,11 @@ static int imagex_export(int argc, const char **argv) if (!S_ISREG(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) { imagex_error("`%s' is not a regular file", dest_wimfile); - goto done; + goto out; } ret = wimlib_open_wim(dest_wimfile, open_flags, &dest_w); if (ret != 0) - goto done; + goto out; if (compression_type_specified && compression_type != wimlib_get_compression_type(dest_w)) { @@ -908,7 +916,7 @@ static int imagex_export(int argc, const char **argv) "not the same as that used in the " "destination WIM"); ret = -1; - goto done; + goto out; } compression_type = wimlib_get_compression_type(dest_w); } else { @@ -917,23 +925,32 @@ static int imagex_export(int argc, const char **argv) if (errno == ENOENT) { ret = wimlib_create_new_wim(compression_type, &dest_w); if (ret != 0) - goto done; + goto out; } else { imagex_error_with_errno("Cannot stat file `%s'", dest_wimfile); - goto done; + goto out; } } image = wimlib_resolve_image(src_w, src_image_num_or_name); ret = verify_image_exists(image); if (ret != 0) - goto done; + goto out; + + if (swm_glob) { + ret = open_swms_from_glob(swm_glob, src_wimfile, open_flags, + &additional_swms, + &num_additional_swms); + if (ret != 0) + goto out; + } ret = wimlib_export_image(src_w, image, dest_w, dest_name, dest_desc, - export_flags); + export_flags, additional_swms, + num_additional_swms); if (ret != 0) - goto done; + goto out; if (wim_is_new) @@ -941,9 +958,12 @@ static int imagex_export(int argc, const char **argv) write_flags); else ret = wimlib_overwrite(dest_w, write_flags); -done: +out: wimlib_free(src_w); wimlib_free(dest_w); + if (additional_swms) + for (unsigned i = 0; i < num_additional_swms; i++) + wimlib_free(additional_swms[i]); return ret; } diff --git a/src/join.c b/src/join.c index 07cbfa97..a1639e36 100644 --- a/src/join.c +++ b/src/join.c @@ -138,6 +138,21 @@ int verify_swm_set(WIMStruct *w, WIMStruct **additional_swms, return 0; } +/* + * Joins lookup tables from the parts of a split WIM. + * + * @w specifies the first part, while @additional_swms and @num_additional_swms + * specify an array of points to the WIMStruct's for additional split WIM parts. + * + * On success, 0 is returned on a pointer to the joined lookup table is returned + * in @table_ret. + * + * The reason we join the lookup tables is so: + * - We only have to search one lookup table to find the location of a + * resource in the entire split WIM. + * - Each lookup table entry will have a pointer to its split WIM part (and + * a part number field, although we don't really use it). + */ int new_joined_lookup_table(WIMStruct *w, WIMStruct **additional_swms, unsigned num_additional_swms, diff --git a/src/lookup_table.h b/src/lookup_table.h index f0ee25b7..6fc5ae50 100644 --- a/src/lookup_table.h +++ b/src/lookup_table.h @@ -41,55 +41,97 @@ struct ntfs_location { * * It is used to find data streams for files in the WIM. * - * The lookup_table_entry for a given dentry in the WIM is found using the SHA1 - * message digest field. + * Metadata resources and reparse point data buffers will also have lookup table + * entries associated with the data. + * + * The lookup_table_entry for a given dentry or alternate stream entry in the + * WIM is found using the SHA1 message digest field. */ struct lookup_table_entry { /* List of lookup table entries in this hash bucket */ struct hlist_node hash_list; - /* @resource_entry is read from the lookup table in the WIM - * file; it says where to find the file resource in the WIM - * file, and whether it is compressed or not. */ + /* Location and size of the stream in the WIM, whether it is compressed + * or not, and whether it's a metadata resource or not. This is an + * on-disk field. */ struct resource_entry resource_entry; - /* Currently ignored; set to 1 in new lookup table entries. */ + /* Specifies which part of the split WIM the resource is located in. + * This is on on-disk field. + * + * In stand-alone WIMs, this must be 1. + * + * In split WIMs, every split WIM part has its own lookup table, and in + * read_lookup_table() it's currently expected that the part number of + * each lookup table entry in a split WIM part's lookup table is the + * same as the part number of that split WIM part. So this makes this + * field redundant since we store a pointer to the corresponding + * WIMStruct in the lookup table entry anyway. + */ u16 part_number; - /* If %true, this lookup table entry corresponds to a symbolic link - * reparse buffer. @symlink_reparse_data_buf will give the target of - * the symbolic link. */ + /* An enumerated type that identifies where the stream corresponding to + * this lookup table entry is actually located. + * + * Obviously if we open a WIM and read its lookup table, the location is + * set to RESOURCE_IN_WIM since all the streams will initially be + * located in the WIM. However, to deal with problems such as image + * capture and image mount, we allow the actual location of the stream + * to be somewhere else, such as an external file. + */ enum { + /* The lookup table entry does not correspond to a stream (this + * state should exist only temporarily) */ RESOURCE_NONEXISTENT = 0, + + /* The stream resource is located in a WIM file. The WIMStruct + * for the WIM file will be pointed to by the @wim member. */ RESOURCE_IN_WIM, + + /* The stream resource is located in an external file. The + * name of the file will be provided by @file_on_disk member. + * In addition, if @file_on_disk_fp is not NULL, it will be an + * open FILE * to the file. */ RESOURCE_IN_FILE_ON_DISK, + + /* The stream resource is located in an external file in the + * staging directory for a read-write mount. */ RESOURCE_IN_STAGING_FILE, + + /* The stream resource is directly attached in an in-memory + * buffer pointed to by @attached_buffer. */ RESOURCE_IN_ATTACHED_BUFFER, + + /* The stream resource is located in an NTFS volume. It is + * identified by volume, filename, data stream name, and by + * whether it is a reparse point or not. @ntfs_loc points to a + * structure containing this information. */ RESOURCE_IN_NTFS_VOLUME, } resource_location; - /* Number of times this lookup table entry is referenced by dentries. */ + /* (On-disk field) + * Number of times this lookup table entry is referenced by dentries. + * Unfortunately, this field is not always set correctly in Microsoft's + * WIMs, so we have no choice but to fix it if more references to the + * lookup table entry are found than stated here. */ u32 refcnt; union { - /* SHA1 hash of the file resource pointed to by this lookup - * table entry */ + /* (On-disk field) SHA1 message digest of the stream referenced + * by this lookup table entry */ u8 hash[SHA1_HASH_SIZE]; - /* First 4 or 8 bytes of the SHA1 hash, used for inserting the - * entry into the hash table. Since the SHA1 hashes can be - * considered random, we don't really need the full 20 byte hash - * just to insert the entry in a hash table. */ + /* First 4 or 8 bytes of the SHA1 message digest, used for + * inserting the entry into the hash table. Since the SHA1 + * message digest can be considered random, we don't really need + * the full 20 byte hash just to insert the entry in a hash + * table. */ size_t hash_short; }; - /* If @file_on_disk != NULL, the file resource indicated by this lookup - * table entry is not in the WIM file, but rather a file on disk; this - * occurs for files that are added to the WIM. In that case, - * file_on_disk is the name of the file in the outside filesystem. - * It will not be compressed, and its size will be given by - * resource_entry.size and resource_entry.original_size. */ + /* Pointers to somewhere where the stream is actually located. See the + * comments for the @resource_location field above. */ union { WIMStruct *wim; char *file_on_disk; @@ -100,14 +142,25 @@ struct lookup_table_entry { #endif }; union { + /* Temporary field for creating a singly linked list. Shouldn't + * really be here */ struct lookup_table_entry *next_lte_in_swm; + + /* @file_on_disk_fp and @attr are both used to cache file/stream + * handles so we don't have re-open them on every read */ FILE *file_on_disk_fp; #ifdef WITH_NTFS_3G struct _ntfs_attr *attr; #endif }; #ifdef WITH_FUSE - /* File descriptors table for this data stream */ + /* File descriptors table for this data stream. This is used if the WIM + * is mounted. Basically, each time a file is open()ed, a new file + * descriptor is added here, and each time a file is close()ed, the file + * descriptor is gotten rid of. If the stream is opened for writing, it + * will be extracted to the staging directory and there will be an + * actual native file descriptor associated with each "wimlib file + * descriptor". */ u16 num_opened_fds; u16 num_allocated_fds; struct wimlib_fd **fds; @@ -115,22 +168,31 @@ struct lookup_table_entry { /* When a WIM file is written, out_refcnt starts at 0 and is incremented * whenever the file resource pointed to by this lookup table entry - * needs to be written. Naturally, the file resource only need to be - * written when out_refcnt is 0. Incrementing it further is needed to - * find the correct reference count to write to the lookup table in the - * output file, which may be less than the regular refcnt if not all - * images in the WIM file are written. - * - * output_resource_entry is the struct resource_entry for the position of the - * file resource when written to the output file. */ + * needs to be written. The file resource only need to be written when + * out_refcnt is nonzero, since otherwise it is not referenced by any + * dentries. */ u32 out_refcnt; + union { + /* When a WIM file is written, @output_resource_entry is filled + * in with the resource entry for the output WIM. This will not + * necessarily be the same as the @resource_entry since: + * - The stream may have a different offset in the new WIM + * - The stream may have a different compressed size in the + * new WIM if the compression type changed + */ struct resource_entry output_resource_entry; + + /* This field is used for the special hardlink or symlink image + * application mode. In these mode, all identical files are + * linked together, and @extracted_file will be set to the + * filename of the first extracted file containing this stream. + * */ char *extracted_file; }; /* Circular linked list of streams that share the same lookup table - * entry + * entry. * * This list of streams may include streams from different hard link * sets that happen to be the same. */ diff --git a/src/modify.c b/src/modify.c index 5223b819..8519881d 100644 --- a/src/modify.c +++ b/src/modify.c @@ -267,16 +267,12 @@ static int add_lte_to_dest_wim(struct dentry *dentry, void *arg) if (dest_lte) { dest_lte->refcnt++; } else { - dest_lte = new_lookup_table_entry(); + dest_lte = MALLOC(sizeof(struct lookup_table_entry)); if (!dest_lte) return WIMLIB_ERR_NOMEM; - dest_lte->resource_location = RESOURCE_IN_WIM; - dest_lte->wim = src_wim; - memcpy(&dest_lte->resource_entry, - &src_lte->resource_entry, - sizeof(struct resource_entry)); - copy_hash(dest_lte->hash, - dentry_stream_hash_unresolved(dentry, i)); + memcpy(dest_lte, src_lte, sizeof(struct lookup_table_entry)); + dest_lte->part_number = 1; + dest_lte->refcnt = 1; lookup_table_insert(dest_wim->lookup_table, dest_lte); } } @@ -356,19 +352,22 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, WIMStruct *dest_wim, const char *dest_name, const char *dest_description, - int flags) + int flags, + WIMStruct **additional_swms, + unsigned num_additional_swms) { int i; int ret; struct dentry *root; struct wim_pair wims; struct wim_security_data *sd; + struct lookup_table *joined_tab, *src_wim_tab_save; if (!src_wim || !dest_wim) return WIMLIB_ERR_INVALID_PARAM; - if (src_wim->hdr.total_parts != 1 || src_wim->hdr.total_parts != 1) { - ERROR("Exporting an image to or from a split WIM is " + if (dest_wim->hdr.total_parts != 1) { + ERROR("Exporting an image to a split WIM is " "unsupported"); return WIMLIB_ERR_SPLIT_UNSUPPORTED; } @@ -406,7 +405,9 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, ret = wimlib_export_image(src_wim, i, dest_wim, NULL, dest_description, - export_flags); + export_flags, + additional_swms, + num_additional_swms); if (ret != 0) return ret; } @@ -416,13 +417,6 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, } } - ret = wimlib_select_image(src_wim, src_image); - if (ret != 0) { - ERROR("Could not select image %d from the WIM `%s' " - "to export it", src_image, src_wim->filename); - return ret; - } - if (!dest_name) { dest_name = wimlib_get_image_name(src_wim, src_image); DEBUG("Using name `%s' for source image %d", @@ -437,12 +431,32 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, return WIMLIB_ERR_IMAGE_NAME_COLLISION; } + ret = verify_swm_set(src_wim, additional_swms, num_additional_swms); + if (ret != 0) + return ret; + + if (num_additional_swms) { + ret = new_joined_lookup_table(src_wim, additional_swms, + num_additional_swms, + &joined_tab); + if (ret != 0) + return ret; + src_wim_tab_save = src_wim->lookup_table; + src_wim->lookup_table = joined_tab; + } + + ret = wimlib_select_image(src_wim, src_image); + if (ret != 0) { + ERROR("Could not select image %d from the WIM `%s' " + "to export it", src_image, src_wim->filename); + goto out; + } /* Cleaning up here on failure would be hard. For example, we could * fail to allocate memory in add_lte_to_dest_wim(), * leaving the lookup table entries in the destination WIM in an * inconsistent state. Until these issues can be resolved, - * wimlib_export_image() is documented as leaving dest_wim is an + * wimlib_export_image() is documented as leaving dest_wim in an * indeterminate state. */ root = wim_root_dentry(src_wim); sd = wim_security_data(src_wim); @@ -451,10 +465,10 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, wims.dest_wim = dest_wim; ret = for_dentry_in_tree(root, add_lte_to_dest_wim, &wims); if (ret != 0) - return ret; + goto out; ret = add_new_dentry_tree(dest_wim, root, sd); if (ret != 0) - return ret; + goto out; sd->refcnt++; if (flags & WIMLIB_EXPORT_FLAG_BOOT) { @@ -462,8 +476,14 @@ WIMLIBAPI int wimlib_export_image(WIMStruct *src_wim, dest_wim->hdr.boot_idx = dest_wim->hdr.image_count; } - return xml_export_image(src_wim->wim_info, src_image, &dest_wim->wim_info, - dest_name, dest_description); + ret = xml_export_image(src_wim->wim_info, src_image, &dest_wim->wim_info, + dest_name, dest_description); +out: + if (num_additional_swms) { + free_lookup_table(src_wim->lookup_table); + src_wim->lookup_table = src_wim_tab_save; + } + return ret; } /* diff --git a/src/wimlib.h b/src/wimlib.h index 94a0f9d9..05681b3a 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -514,8 +514,8 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Copies an image, or all the images, from a WIM file, into another WIM file. * * @param src_wim - * Pointer to the ::WIMStruct for a WIM file that contains the image(s) - * being exported. + * Pointer to the ::WIMStruct for a stand-alone WIM or part 1 of a split + * WIM that contains the image(s) being exported. * @param src_image * The image to export from @a src_wim. Can be the number of an image, or * ::WIM_ALL_IMAGES to export all images. @@ -541,6 +541,16 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * ::WIMLIB_EXPORT_FLAG_BOOT is valid only if one of the exported images is * currently marked as bootable in @a src_wim; if that is the case, then * that image is marked as bootable in the destination WIM. + * @param additional_swms + * Array of pointers to the ::WIMStruct for each additional part in the + * split WIM. Ignored if @a num_additional_swms is 0. The pointers do not + * need to be in any particular order, but they must include all parts of + * the split WIM other than the first part, which must be provided in the + * @a wim parameter. + * @param num_additional_swms + * Number of additional WIM parts provided in the @a additional_swms array. + * 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. * * @return 0 on success; nonzero on error. On error, @dest_wim is left in an * indeterminate state and should be freed with wimlib_free(). @@ -570,13 +580,20 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_READ * Could not read the metadata resource for @a src_image from @a src_wim. + * @retval ::WIMLIB_ERR_SPLIT_INVALID + * The source WIM is a split WIM, but the parts specified do not form a + * complete split WIM because they do not include all the parts of the + * original WIM, there are duplicate parts, or not all the parts have the + * same GUID and compression type. * @retval ::WIMLIB_ERR_SPLIT_UNSUPPORTED - * @a src_wim or @a dest_wim is part of a split WIM. Exporting an image - * from or to a split WIM is unsupported. + * @a dest_wim is part of a split WIM. Exporting an image to a split WIM + * is unsupported. */ extern int wimlib_export_image(WIMStruct *src_wim, int src_image, WIMStruct *dest_wim, const char *dest_name, - const char *dest_description, int flags); + const char *dest_description, int flags, + WIMStruct **additional_swms, + unsigned num_additional_swms); /** * Extracts an image, or all images, from a standalone or split WIM file. -- 2.43.0 From 00bb6a68b1fe07aae4255a37bdebf8350c43bc17 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 31 Aug 2012 23:47:01 -0500 Subject: [PATCH 04/16] image default names --- programs/imagex.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/programs/imagex.c b/programs/imagex.c index fac692f0..78eebaaa 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0])) @@ -379,6 +380,7 @@ static int imagex_append(int argc, const char **argv) WIMStruct *w; int ret; int cur_image; + char *default_name; for_opt(c, append_options) { switch (c) { @@ -414,7 +416,12 @@ static int imagex_append(int argc, const char **argv) } dir = argv[0]; wimfile = argv[1]; - name = (argc >= 3) ? argv[2] : path_basename(dir); + + char dir_copy[strlen(dir) + 1]; + memcpy(dir_copy, dir, strlen(dir) + 1); + default_name = basename(dir_copy); + + name = (argc >= 3) ? argv[2] : default_name; desc = (argc >= 4) ? argv[3] : NULL; if (config_file) { @@ -604,6 +611,7 @@ static int imagex_capture(int argc, const char **argv) size_t config_len = 0; WIMStruct *w; int cur_image; + char *default_name; int ret; for_opt(c, capture_options) { @@ -646,7 +654,12 @@ static int imagex_capture(int argc, const char **argv) } dir = argv[0]; wimfile = argv[1]; - name = (argc >= 3) ? argv[2] : dir; + + char dir_copy[strlen(dir) + 1]; + memcpy(dir_copy, dir, strlen(dir) + 1); + default_name = basename(dir_copy); + + name = (argc >= 3) ? argv[2] : default_name; desc = (argc >= 4) ? argv[3] : NULL; if (config_file) { -- 2.43.0 From e03410487e77ea4aa8af8ebcf279f4ccbcd9ccba Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 00:02:52 -0500 Subject: [PATCH 05/16] imagex capture and append consolidation --- programs/imagex.c | 193 +++++++++------------------------------------- src/wim.c | 2 +- 2 files changed, 36 insertions(+), 159 deletions(-) diff --git a/programs/imagex.c b/programs/imagex.c index 78eebaaa..fc89cdfe 100644 --- a/programs/imagex.c +++ b/programs/imagex.c @@ -108,15 +108,6 @@ static const struct option common_options[] = { {NULL, 0, NULL, 0}, }; -static const struct option append_options[] = { - {"boot", no_argument, NULL, 'b'}, - {"check", no_argument, NULL, 'c'}, - {"config", required_argument, NULL, 'C'}, - {"dereference", no_argument, NULL, 'L'}, - {"flags", required_argument, NULL, 'f'}, - {"verbose", no_argument, NULL, 'v'}, - {NULL, 0, NULL, 0}, -}; static const struct option apply_options[] = { {"check", no_argument, NULL, 'c'}, {"hardlink", no_argument, NULL, 'h'}, @@ -125,7 +116,7 @@ static const struct option apply_options[] = { {"ref", required_argument, NULL, 'r'}, {NULL, 0, NULL, 0}, }; -static const struct option capture_options[] = { +static const struct option capture_or_append_options[] = { {"boot", no_argument, NULL, 'b'}, {"check", no_argument, NULL, 'c'}, {"compress", required_argument, NULL, 'x'}, @@ -268,7 +259,7 @@ static int get_compression_type(const char *optarg) } } -static const char *file_get_contents(const char *filename, size_t *len_ret) +static char *file_get_contents(const char *filename, size_t *len_ret) { struct stat stbuf; char *buf; @@ -363,120 +354,6 @@ out: return ret; } -static int imagex_append(int argc, const char **argv) -{ - int c; - const char *flags_element = NULL; - int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; - int add_image_flags = 0; - int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; - const char *dir; - const char *wimfile; - const char *name; - const char *desc; - const char *config_file = NULL; - const char *config_str = NULL; - size_t config_len = 0; - WIMStruct *w; - int ret; - int cur_image; - char *default_name; - - for_opt(c, append_options) { - switch (c) { - case 'b': - add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT; - break; - case 'c': - open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; - write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; - break; - case 'C': - config_file = optarg; - break; - case 'f': - flags_element = optarg; - break; - case 'L': - add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE; - break; - case 'v': - add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; - break; - default: - usage(APPEND); - return -1; - } - } - argc -= optind; - argv += optind; - if (argc < 2 || argc > 4) { - usage(APPEND); - return -1; - } - dir = argv[0]; - wimfile = argv[1]; - - char dir_copy[strlen(dir) + 1]; - memcpy(dir_copy, dir, strlen(dir) + 1); - default_name = basename(dir_copy); - - name = (argc >= 3) ? argv[2] : default_name; - desc = (argc >= 4) ? argv[3] : NULL; - - if (config_file) { - config_str = file_get_contents(config_file, &config_len); - if (!config_str) - return -1; - } - - ret = wimlib_open_wim(wimfile, open_flags, &w); - if (ret != 0) - return ret; - -#ifdef WITH_NTFS_3G - struct stat stbuf; - - ret = stat(dir, &stbuf); - if (ret == 0) { - if (S_ISBLK(stbuf.st_mode) || S_ISREG(stbuf.st_mode)) { - const char *ntfs_device = dir; - printf("Capturing WIM image NTFS filesystem on `%s'\n", - ntfs_device); - ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device, - name, - config_str, - config_len, - add_image_flags); - goto out_write; - } - } else { - if (errno != ENOENT) - imagex_error_with_errno("Failed to stat `%s'", dir); - } -#endif - ret = wimlib_add_image(w, dir, name, config_str, config_len, - add_image_flags); - -out_write: - if (ret != 0) - goto out; - cur_image = wimlib_get_num_images(w); - if (desc) { - ret = wimlib_set_image_descripton(w, cur_image, desc); - if (ret != 0) - goto out; - } - if (flags_element) { - ret = wimlib_set_image_flags(w, cur_image, flags_element); - if (ret != 0) - goto out; - } - ret = wimlib_overwrite(w, write_flags); -out: - wimlib_free(w); - return ret; -} /* Extract one image, or all images, from a WIM file into a directory. */ static int imagex_apply(int argc, const char **argv) @@ -593,33 +470,34 @@ out: return ret; } - -/* Create a WIM file from a directory. */ -static int imagex_capture(int argc, const char **argv) +static int imagex_capture_or_append(int argc, const char **argv) { int c; + int open_flags = WIMLIB_OPEN_FLAG_SHOW_PROGRESS; int add_image_flags = 0; int write_flags = WIMLIB_WRITE_FLAG_SHOW_PROGRESS; int compression_type = WIM_COMPRESSION_TYPE_XPRESS; - const char *flags_element = NULL; const char *dir; const char *wimfile; const char *name; const char *desc; + const char *flags_element = NULL; const char *config_file = NULL; - const char *config_str = NULL; + char *config_str = NULL; size_t config_len = 0; - WIMStruct *w; + WIMStruct *w = NULL; + int ret; int cur_image; char *default_name; - int ret; + int cmd = strcmp(argv[0], "append") ? CAPTURE : APPEND; - for_opt(c, capture_options) { + for_opt(c, capture_or_append_options) { switch (c) { case 'b': add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_BOOT; break; case 'c': + open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY; write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY; break; case 'C': @@ -633,23 +511,21 @@ static int imagex_capture(int argc, const char **argv) case 'f': flags_element = optarg; break; - case 'v': - add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; - write_flags |= WIMLIB_WRITE_FLAG_VERBOSE; - break; case 'L': add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE; break; + case 'v': + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_VERBOSE; + break; default: - usage(CAPTURE); + usage(cmd); return -1; } } - argc -= optind; argv += optind; if (argc < 2 || argc > 4) { - usage(CAPTURE); + usage(cmd); return -1; } dir = argv[0]; @@ -668,9 +544,12 @@ static int imagex_capture(int argc, const char **argv) return -1; } - ret = wimlib_create_new_wim(compression_type, &w); + if (cmd == APPEND) + ret = wimlib_open_wim(wimfile, open_flags, &w); + else + ret = wimlib_create_new_wim(compression_type, &w); if (ret != 0) - return ret; + goto out; #ifdef WITH_NTFS_3G struct stat stbuf; @@ -689,18 +568,19 @@ static int imagex_capture(int argc, const char **argv) goto out_write; } } else { - if (errno != ENOENT) + if (errno != ENOENT) { imagex_error_with_errno("Failed to stat `%s'", dir); + ret = -1; + goto out; + } } #endif - ret = wimlib_add_image(w, dir, name, config_str, - config_len, add_image_flags); + ret = wimlib_add_image(w, dir, name, config_str, config_len, + add_image_flags); out_write: - if (ret != 0) { - imagex_error("Failed to add the image `%s'", dir); + if (ret != 0) goto out; - } cur_image = wimlib_get_num_images(w); if (desc) { ret = wimlib_set_image_descripton(w, cur_image, desc); @@ -712,12 +592,15 @@ out_write: if (ret != 0) goto out; } - - ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags); + if (cmd == APPEND) + ret = wimlib_overwrite(w, write_flags); + else + ret = wimlib_write(w, wimfile, WIM_ALL_IMAGES, write_flags); if (ret != 0) imagex_error("Failed to write the WIM file `%s'", wimfile); out: wimlib_free(w); + free(config_str); return ret; } @@ -1158,12 +1041,6 @@ static int imagex_info(int argc, const char **argv) wimlib_print_available_images(w, image); if (metadata) { - if (total_parts != 1 && part_number != 1) { - imagex_error("Select part 1 of this %d-part WIM " - "to see the image metadata", - total_parts); - return WIMLIB_ERR_SPLIT_UNSUPPORTED; - } ret = wimlib_print_metadata(w, image); if (ret != 0) goto done; @@ -1452,9 +1329,9 @@ struct imagex_command { p != &imagex_commands[ARRAY_LEN(imagex_commands)]; p++) static struct imagex_command imagex_commands[] = { - {"append", imagex_append, APPEND}, + {"append", imagex_capture_or_append, APPEND}, {"apply", imagex_apply, APPLY}, - {"capture", imagex_capture, CAPTURE}, + {"capture", imagex_capture_or_append, CAPTURE}, {"delete", imagex_delete, DELETE}, {"dir", imagex_dir, DIR}, {"export", imagex_export, EXPORT}, diff --git a/src/wim.c b/src/wim.c index 5d5c8fef..2c67ebab 100644 --- a/src/wim.c +++ b/src/wim.c @@ -331,7 +331,7 @@ WIMLIBAPI int wimlib_print_metadata(WIMStruct *w, int image) if (!w) return WIMLIB_ERR_INVALID_PARAM; if (w->hdr.part_number != 1) { - ERROR("We cannot show the metadata from part %hu of a %hu-part split WIM", + ERROR("We cannot show the metadata from part %hu of a %hu-part split WIM.", w->hdr.part_number, w->hdr.total_parts); ERROR("Select the first part of the split WIM to see the metadata."); return WIMLIB_ERR_SPLIT_UNSUPPORTED; -- 2.43.0 From a1c93f60d17225fba2268243fcbda48b0cc0ec64 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 00:21:08 -0500 Subject: [PATCH 06/16] NTFS apply update Allow the use of ntfs_xattr_system_setxattr() as an alternative to ntfs_set_inode_security() and ntfs_set_inode_attributes(). --- src/ntfs-apply.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 7ae7fa17..cfeee3f6 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -35,10 +35,9 @@ #include #include #include -#include +#include /* security.h before xattrs.h */ +#include #include -#include -#include #include #include @@ -235,34 +234,58 @@ static int wim_apply_hardlink_ntfs(const struct dentry *from_dentry, return ret; } +/*#define HAVE_NTFS_INODE_FUNCTIONS*/ + static int apply_file_attributes_and_security_data(ntfs_inode *ni, + ntfs_inode *dir_ni, const struct dentry *dentry, const WIMStruct *w) { DEBUG("Setting NTFS file attributes on `%s' to %#"PRIx32, dentry->full_path_utf8, dentry->attributes); - if (ntfs_set_inode_attributes(ni, dentry->attributes)) { + int ret; +#ifdef HAVE_NTFS_INODE_FUNCTIONS + ret = ntfs_set_inode_attributes(ni, dentry->attributes); +#else + struct SECURITY_CONTEXT ctx; + u32 attributes_le32; + attributes_le32 = cpu_to_le32(dentry->attributes); + memset(&ctx, 0, sizeof(ctx)); + ctx.vol = ni->vol; + ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ATTRIB, + ni, dir_ni, + (const char*)&attributes_le32, + sizeof(u32), 0); +#endif + if (ret != 0) { ERROR("Failed to set NTFS file attributes on `%s'", dentry->full_path_utf8); return WIMLIB_ERR_NTFS_3G; } - if (dentry->security_id != -1) { const struct wim_security_data *sd; + const char *descriptor; sd = wim_const_security_data(w); wimlib_assert(dentry->security_id < sd->num_entries); + descriptor = sd->descriptors[dentry->security_id]; DEBUG("Applying security descriptor %d to `%s'", dentry->security_id, dentry->full_path_utf8); + + #ifdef HAVE_NTFS_INODE_FUNCTIONS u32 selection = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; + ret = ntfs_set_inode_security(ni, selection, descriptor); + #else + ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL, + ni, dir_ni, descriptor, + sd->sizes[dentry->security_id], 0); + #endif - if (ntfs_set_inode_security(ni, selection, - (const char*)sd->descriptors[dentry->security_id])) - { + if (ret != 0) { ERROR_WITH_ERRNO("Failed to set security data on `%s'", dentry->full_path_utf8); return WIMLIB_ERR_NTFS_3G; @@ -464,7 +487,7 @@ static int do_wim_apply_dentry_ntfs(struct dentry *dentry, ntfs_inode *dir_ni, } - ret = apply_file_attributes_and_security_data(ni, dentry, w); + ret = apply_file_attributes_and_security_data(ni, dir_ni, dentry, w); if (ret != 0) goto out_close_dir_ni; @@ -574,7 +597,7 @@ static int wim_apply_root_dentry_ntfs(const struct dentry *dentry, ERROR_WITH_ERRNO("Could not find root NTFS inode"); return WIMLIB_ERR_NTFS_3G; } - ret = apply_file_attributes_and_security_data(ni, dentry, w); + ret = apply_file_attributes_and_security_data(ni, ni, dentry, w); if (ntfs_inode_close(ni) != 0) { ERROR_WITH_ERRNO("Failed to close NTFS inode for root " "directory"); -- 2.43.0 From 20dac4a44f74de14a71c2445d23f79cf56728cfe Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 00:38:52 -0500 Subject: [PATCH 07/16] NTFS capture update Allow the use of ntfs_xattr_system_getattr() as an alternative to ntfs_get_inode_security() and ntfs_get_inode_attributes(). --- src/ntfs-apply.c | 2 +- src/ntfs-capture.c | 54 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index cfeee3f6..23526810 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -48,7 +48,7 @@ struct ntfs_apply_args { }; -#ifndef WITH_NEW_NTFS_3G +#if 0 extern int ntfs_set_inode_security(ntfs_inode *ni, u32 selection, const char *attr); extern int ntfs_set_inode_attributes(ntfs_inode *ni, u32 attrib); diff --git a/src/ntfs-capture.c b/src/ntfs-capture.c index dd55d97e..cd534a21 100644 --- a/src/ntfs-capture.c +++ b/src/ntfs-capture.c @@ -37,13 +37,14 @@ #include #include #include -#include +#include /* security.h before xattrs.h */ +#include #include #include #include #include -#ifndef WITH_NEW_NTFS_3G +#if 0 extern int ntfs_get_inode_security(ntfs_inode *ni, u32 selection, char *buf, u32 buflen, u32 *psize); @@ -489,6 +490,8 @@ static int change_dentry_short_name(struct dentry *dentry, return 0; } +/*#define HAVE_NTFS_INODE_FUNCTIONS*/ + /* Recursively build a WIM dentry tree corresponding to a NTFS volume. * At the same time, update the WIM lookup table with lookup table entries for * the NTFS streams, and build an array of security descriptors. @@ -513,7 +516,21 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, struct dentry *root; mrec_flags = ni->mrec->flags; +#ifdef HAVE_NTFS_INODE_FUNCTIONS attributes = ntfs_get_inode_attributes(ni); +#else + struct SECURITY_CONTEXT ctx; + memset(&ctx, 0, sizeof(ctx)); + ctx.vol = ni->vol; + ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ATTRIB, + ni, dir_ni, (char *)&attributes, + sizeof(u32)); + if (ret != 4) { + ERROR_WITH_ERRNO("Failed to get NTFS attributes from `%s'", + path); + return WIMLIB_ERR_NTFS_3G; + } +#endif if (exclude_path(path, config, false)) { if (flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) { @@ -598,6 +615,7 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, if (ret != 0) return ret; +#ifdef HAVE_NTFS_INODE_FUNCTIONS ret = ntfs_get_inode_security(ni, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | @@ -618,7 +636,7 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, } else { if (ret > 0) { /*print_security_descriptor(sd, sd_size);*/ - root->security_id = sd_set_add_sd(sd_set, sd, sd_size); + root->security_id = sd_set_add_sd(sd_set, sd, ret); if (root->security_id == -1) { ERROR("Out of memory"); return WIMLIB_ERR_NOMEM; @@ -631,6 +649,36 @@ static int build_dentry_tree_ntfs_recursive(struct dentry **root_p, } ret = 0; } +#else + char _sd[1]; + char *sd = _sd; + errno = 0; + ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL, + ni, dir_ni, sd, + sizeof(sd)); + if (ret > sizeof(sd)) { + sd = alloca(ret); + ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL, + ni, dir_ni, sd, ret); + } + if (ret > 0) { + root->security_id = sd_set_add_sd(sd_set, sd, ret); + if (root->security_id == -1) { + ERROR("Out of memory"); + return WIMLIB_ERR_NOMEM; + } + DEBUG("Added security ID = %u for `%s'", + root->security_id, path); + ret = 0; + } else if (ret < 0) { + ERROR_WITH_ERRNO("Failed to get security information from " + "`%s'", path); + ret = WIMLIB_ERR_NTFS_3G; + } else { + root->security_id = -1; + DEBUG("No security ID for `%s'", path); + } +#endif return ret; } -- 2.43.0 From ae01a7b97e80bc86a53d9c4a377116fb293c3655 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 00:45:14 -0500 Subject: [PATCH 08/16] Get rid of code clone from libntfs-3g --- Makefile.am | 7 - README | 6 +- config.h.in | 3 - configure.ac | 19 - src/ntfs-3g_security.c | 5163 ---------------------------------------- 5 files changed, 1 insertion(+), 5197 deletions(-) delete mode 100644 src/ntfs-3g_security.c diff --git a/Makefile.am b/Makefile.am index 9c36d5a1..f05e58dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,13 +53,6 @@ libwim_la_SOURCES = \ src/xpress-decomp.c \ src/xpress.h -if WITH_NTFS_3G -if !WITH_NEW_NTFS_3G -libwim_la_SOURCES += src/ntfs-3g_security.c -endif -endif - - EXTRA_libwim_la_SOURCES = src/sha1-ssse3.asm libwim_la_DEPENDENCIES = $(SSSE3_SHA1_OBJ) STRIP_FPIC = sh $(top_srcdir)/build-aux/strip_fPIC.sh diff --git a/README b/README index 91deb1c0..364cb77f 100644 --- a/README +++ b/README @@ -94,11 +94,7 @@ server for PXE booting. See the main page `doc/mkwinpeiso.1' for more details. * libntfs-3g Unless configured with --without-ntfs-3g, wimlib requires the library - and headers for libntfs-3g to be installed. Currently, the libntfs-3g - version dated 2012-1-15 is required because I've cloned some of the code - from the library, and it needs to compiled against the same version. - I'm hoping to be able to use future version of libntfs-3g without code - cloning after submitting some patches, however. + and headers for libntfs-3g to be installed. * cdrkit (optional) * mtools (optional) diff --git a/config.h.in b/config.h.in index ecd767d3..db9f0e2d 100644 --- a/config.h.in +++ b/config.h.in @@ -106,9 +106,6 @@ /* Define to 1 if using libcrypto SHA1 */ #undef WITH_LIBCRYPTO -/* Define to 1 to use patched upstream NTFS-3g instead of version 2012-1-15 */ -#undef WITH_NEW_NTFS_3G - /* Define to 1 to enable support for NTFS-specific information */ #undef WITH_NTFS_3G diff --git a/configure.ac b/configure.ac index 5dcc2ff1..c2156614 100644 --- a/configure.ac +++ b/configure.ac @@ -163,25 +163,6 @@ AM_CONDITIONAL([WITH_NTFS_3G], [test "x$WITH_NTFS_3G" = "xyes"]) AC_SUBST([LIBNTFS_3G_LDADD], [$LIBNTFS_3G_LDADD]) AC_SUBST([LIBNTFS_3G_CFLAGS], [$LIBNTFS_3G_CFLAGS]) -AC_MSG_CHECKING([whether to use patched upstream libntfs-3g]) -AC_ARG_WITH([new-ntfs-3g], - [AS_HELP_STRING([--with-new-ntfs-3g], [build using patched upstream - NTFS-3g instead of version 2012-1-15])], - [WITH_NEW_NTFS_3G=$withval], - [WITH_NEW_NTFS_3G=no] - ) - -AC_MSG_RESULT([$WITH_NEW_NTFS_3G]) -if test "x$WITH_NEW_NTFS_3G" = "xyes"; then - if test "x$WITH_NTFS_3G" != "xyes"; then - AC_MSG_ERROR([Cannot use new NTFS-3g if configuring - --without-ntfs-3g]) - fi - AC_DEFINE([WITH_NEW_NTFS_3G], [1], [Define to 1 to use patched upstream - NTFS-3g instead of version 2012-1-15]) -fi -AM_CONDITIONAL([WITH_NEW_NTFS_3G], [test "x$WITH_NEW_NTFS_3G" = "xyes"]) - AC_MSG_CHECKING([whether to include support for mounting WIMs]) AC_ARG_WITH([fuse], AS_HELP_STRING([--without-fuse], [build without libfuse. diff --git a/src/ntfs-3g_security.c b/src/ntfs-3g_security.c deleted file mode 100644 index 7c8dec3c..00000000 --- a/src/ntfs-3g_security.c +++ /dev/null @@ -1,5163 +0,0 @@ -/** - * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project. - * - * Copyright (c) 2004 Anton Altaparmakov - * Copyright (c) 2005-2006 Szabolcs Szakacsits - * Copyright (c) 2006 Yura Pakhuchiy - * Copyright (c) 2007-2010 Jean-Pierre Andre - * - * This program/include file 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 2 of the License, or - * (at your option) any later version. - * - * This program/include file 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 (in the main directory of the NTFS-3G - * distribution in the file COPYING); if not, write to the Free Software - * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef ENABLE_XATTR -#define HAVE_SETXATTR -#endif - -#include - -#include -#include -#include -#include -#include -#ifdef HAVE_SETXATTR -#include -#endif -#ifdef HAVE_SYS_STAT_H -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * JPA NTFS constants or structs - * should be moved to layout.h - */ - -#define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */ -#define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */ -#define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */ -#define FIRST_SECURITY_ID 0x100 /* Lowest security id */ - - /* Mask for attributes which can be forced */ -#define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \ - | FILE_ATTR_HIDDEN \ - | FILE_ATTR_SYSTEM \ - | FILE_ATTR_ARCHIVE \ - | FILE_ATTR_TEMPORARY \ - | FILE_ATTR_OFFLINE \ - | FILE_ATTR_NOT_CONTENT_INDEXED ) - -struct SII { /* this is an image of an $SII index entry */ - le16 offs; - le16 size; - le32 fill1; - le16 indexsz; - le16 indexksz; - le16 flags; - le16 fill2; - le32 keysecurid; - - /* did not find official description for the following */ - le32 hash; - le32 securid; - le32 dataoffsl; /* documented as badly aligned */ - le32 dataoffsh; - le32 datasize; -} ; - -struct SDH { /* this is an image of an $SDH index entry */ - le16 offs; - le16 size; - le32 fill1; - le16 indexsz; - le16 indexksz; - le16 flags; - le16 fill2; - le32 keyhash; - le32 keysecurid; - - /* did not find official description for the following */ - le32 hash; - le32 securid; - le32 dataoffsl; - le32 dataoffsh; - le32 datasize; - le32 fill3; - } ; - -/* - * A few useful constants - */ - -static ntfschar sii_stream[] = { const_cpu_to_le16('$'), - const_cpu_to_le16('S'), - const_cpu_to_le16('I'), - const_cpu_to_le16('I'), - const_cpu_to_le16(0) }; -static ntfschar sdh_stream[] = { const_cpu_to_le16('$'), - const_cpu_to_le16('S'), - const_cpu_to_le16('D'), - const_cpu_to_le16('H'), - const_cpu_to_le16(0) }; - -/* - * null SID (S-1-0-0) - */ - -extern const SID *nullsid; - -/* - * The zero GUID. - */ - -static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0), - const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } }; -static const GUID *const zero_guid = &__zero_guid; - -/** - * ntfs_guid_is_zero - check if a GUID is zero - * @guid: [IN] guid to check - * - * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID - * and FALSE otherwise. - */ -BOOL ntfs_guid_is_zero(const GUID *guid) -{ - return (memcmp(guid, zero_guid, sizeof(*zero_guid))); -} - -/** - * ntfs_guid_to_mbs - convert a GUID to a multi byte string - * @guid: [IN] guid to convert - * @guid_str: [OUT] string in which to return the GUID (optional) - * - * Convert the GUID pointed to by @guid to a multi byte string of the form - * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL) - * needs to be able to store at least 37 bytes. - * - * If @guid_str is not NULL it will contain the converted GUID on return. If - * it is NULL a string will be allocated and this will be returned. The caller - * is responsible for free()ing the string in that case. - * - * On success return the converted string and on failure return NULL with errno - * set to the error code. - */ -char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str) -{ - char *_guid_str; - int res; - - if (!guid) { - errno = EINVAL; - return NULL; - } - _guid_str = guid_str; - if (!_guid_str) { - _guid_str = (char*)ntfs_malloc(37); - if (!_guid_str) - return _guid_str; - } - res = snprintf(_guid_str, 37, - "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - (unsigned int)le32_to_cpu(guid->data1), - le16_to_cpu(guid->data2), le16_to_cpu(guid->data3), - guid->data4[0], guid->data4[1], - guid->data4[2], guid->data4[3], guid->data4[4], - guid->data4[5], guid->data4[6], guid->data4[7]); - if (res == 36) - return _guid_str; - if (!guid_str) - free(_guid_str); - errno = EINVAL; - return NULL; -} - -/** - * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID - * @sid: [IN] SID for which to determine the maximum string size - * - * Determine the maximum multi byte string size in bytes which is needed to - * store the standard textual representation of the SID pointed to by @sid. - * See ntfs_sid_to_mbs(), below. - * - * On success return the maximum number of bytes needed to store the multi byte - * string and on failure return -1 with errno set to the error code. - */ -int ntfs_sid_to_mbs_size(const SID *sid) -{ - int size, i; - - if (!ntfs_sid_is_valid(sid)) { - errno = EINVAL; - return -1; - } - /* Start with "S-". */ - size = 2; - /* - * Add the SID_REVISION. Hopefully the compiler will optimize this - * away as SID_REVISION is a constant. - */ - for (i = SID_REVISION; i > 0; i /= 10) - size++; - /* Add the "-". */ - size++; - /* - * Add the identifier authority. If it needs to be in decimal, the - * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be - * in hexadecimal, then maximum is 0x665544332211 = 14 characters. - */ - if (!sid->identifier_authority.high_part) - size += 10; - else - size += 14; - /* - * Finally, add the sub authorities. For each we have a "-" followed - * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters. - */ - size += (1 + 10) * sid->sub_authority_count; - /* We need the zero byte at the end, too. */ - size++; - return size * sizeof(char); -} - -/** - * ntfs_sid_to_mbs - convert a SID to a multi byte string - * @sid: [IN] SID to convert - * @sid_str: [OUT] string in which to return the SID (optional) - * @sid_str_size: [IN] size in bytes of @sid_str - * - * Convert the SID pointed to by @sid to its standard textual representation. - * @sid_str (if not NULL) needs to be able to store at least - * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of - * @sid_str if @sid_str is not NULL. - * - * The standard textual representation of the SID is of the form: - * S-R-I-S-S... - * Where: - * - The first "S" is the literal character 'S' identifying the following - * digits as a SID. - * - R is the revision level of the SID expressed as a sequence of digits - * in decimal. - * - I is the 48-bit identifier_authority, expressed as digits in decimal, - * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32. - * - S... is one or more sub_authority values, expressed as digits in - * decimal. - * - * If @sid_str is not NULL it will contain the converted SUID on return. If it - * is NULL a string will be allocated and this will be returned. The caller is - * responsible for free()ing the string in that case. - * - * On success return the converted string and on failure return NULL with errno - * set to the error code. - */ -char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size) -{ - u64 u; - le32 leauth; - char *s; - int i, j, cnt; - - /* - * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will - * check @sid, too. 8 is the minimum SID string size. - */ - if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) { - errno = EINVAL; - return NULL; - } - /* Allocate string if not provided. */ - if (!sid_str) { - cnt = ntfs_sid_to_mbs_size(sid); - if (cnt < 0) - return NULL; - s = (char*)ntfs_malloc(cnt); - if (!s) - return s; - sid_str = s; - /* So we know we allocated it. */ - sid_str_size = 0; - } else { - s = sid_str; - cnt = sid_str_size; - } - /* Start with "S-R-". */ - i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - /* Add the identifier authority. */ - for (u = i = 0, j = 40; i < 6; i++, j -= 8) - u += (u64)sid->identifier_authority.value[i] << j; - if (!sid->identifier_authority.high_part) - i = snprintf(s, cnt, "%lu", (unsigned long)u); - else - i = snprintf(s, cnt, "0x%llx", (unsigned long long)u); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - /* Finally, add the sub authorities. */ - for (j = 0; j < sid->sub_authority_count; j++) { - leauth = sid->sub_authority[j]; - i = snprintf(s, cnt, "-%u", (unsigned int) - le32_to_cpu(leauth)); - if (i < 0 || i >= cnt) - goto err_out; - s += i; - cnt -= i; - } - return sid_str; -err_out: - if (i >= cnt) - i = EMSGSIZE; - else - i = errno; - if (!sid_str_size) - free(sid_str); - errno = i; - return NULL; -} - -/** - * ntfs_generate_guid - generatates a random current guid. - * @guid: [OUT] pointer to a GUID struct to hold the generated guid. - * - * perhaps not a very good random number generator though... - */ -void ntfs_generate_guid(GUID *guid) -{ - unsigned int i; - u8 *p = (u8 *)guid; - - for (i = 0; i < sizeof(GUID); i++) { - p[i] = (u8)(random() & 0xFF); - if (i == 7) - p[7] = (p[7] & 0x0F) | 0x40; - if (i == 8) - p[8] = (p[8] & 0x3F) | 0x80; - } -} - -/** - * ntfs_security_hash - calculate the hash of a security descriptor - * @sd: self-relative security descriptor whose hash to calculate - * @length: size in bytes of the security descritor @sd - * - * Calculate the hash of the self-relative security descriptor @sd of length - * @length bytes. - * - * This hash is used in the $Secure system file as the primary key for the $SDH - * index and is also stored in the header of each security descriptor in the - * $SDS data stream as well as in the index data of both the $SII and $SDH - * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER - * structure. - * - * Return the calculated security hash in little endian. - */ -le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len) -{ - const le32 *pos = (const le32*)sd; - const le32 *end = pos + (len >> 2); - u32 hash = 0; - - while (pos < end) { - hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3); - pos++; - } - return cpu_to_le32(hash); -} - -/* - * Get the first entry of current index block - * cut and pasted form ntfs_ie_get_first() in index.c - */ - -static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih) -{ - return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset)); -} - -/* - * Stuff a 256KB block into $SDS before writing descriptors - * into the block. - * - * This prevents $SDS from being automatically declared as sparse - * when the second copy of the first security descriptor is written - * 256KB further ahead. - * - * Having $SDS declared as a sparse file is not wrong by itself - * and chkdsk leaves it as a sparse file. It does however complain - * and add a sparse flag (0x0200) into field file_attributes of - * STANDARD_INFORMATION of $Secure. This probably means that a - * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse - * files (FILE_ATTR_SPARSE_FILE). - * - * Windows normally does not convert to sparse attribute or sparse - * file. Stuffing is just a way to get to the same result. - */ - -static int entersecurity_stuff(ntfs_volume *vol, off_t offs) -{ - int res; - int written; - unsigned long total; - char *stuff; - - res = 0; - total = 0; - stuff = (char*)ntfs_malloc(STUFFSZ); - if (stuff) { - memset(stuff, 0, STUFFSZ); - do { - written = ntfs_attr_data_write(vol->secure_ni, - STREAM_SDS, 4, stuff, STUFFSZ, offs); - if (written == STUFFSZ) { - total += STUFFSZ; - offs += STUFFSZ; - } else { - errno = ENOSPC; - res = -1; - } - } while (!res && (total < ALIGN_SDS_BLOCK)); - free(stuff); - } else { - errno = ENOMEM; - res = -1; - } - return (res); -} - -/* - * Enter a new security descriptor into $Secure (data only) - * it has to be written twice with an offset of 256KB - * - * Should only be called by entersecurityattr() to ensure consistency - * - * Returns zero if sucessful - */ - -static int entersecurity_data(ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, - le32 hash, le32 keyid, off_t offs, int gap) -{ - int res; - int written1; - int written2; - char *fullattr; - int fullsz; - SECURITY_DESCRIPTOR_HEADER *phsds; - - res = -1; - fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER); - fullattr = (char*)ntfs_malloc(fullsz); - if (fullattr) { - /* - * Clear the gap from previous descriptor - * this could be useful for appending the second - * copy to the end of file. When creating a new - * 256K block, the gap is cleared while writing - * the first copy - */ - if (gap) - memset(fullattr,0,gap); - memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)], - attr,attrsz); - phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap]; - phsds->hash = hash; - phsds->security_id = keyid; - phsds->offset = cpu_to_le64(offs); - phsds->length = cpu_to_le32(fullsz - gap); - written1 = ntfs_attr_data_write(vol->secure_ni, - STREAM_SDS, 4, fullattr, fullsz, - offs - gap); - written2 = ntfs_attr_data_write(vol->secure_ni, - STREAM_SDS, 4, fullattr, fullsz, - offs - gap + ALIGN_SDS_BLOCK); - if ((written1 == fullsz) - && (written2 == written1)) - res = 0; - else - errno = ENOSPC; - free(fullattr); - } else - errno = ENOMEM; - return (res); -} - -/* - * Enter a new security descriptor in $Secure (indexes only) - * - * Should only be called by entersecurityattr() to ensure consistency - * - * Returns zero if sucessful - */ - -static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz, - le32 hash, le32 keyid, off_t offs) -{ - union { - struct { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - int res; - ntfs_index_context *xsii; - ntfs_index_context *xsdh; - struct SII newsii; - struct SDH newsdh; - - res = -1; - /* enter a new $SII record */ - - xsii = vol->secure_xsii; - ntfs_index_ctx_reinit(xsii); - newsii.offs = const_cpu_to_le16(20); - newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20); - newsii.fill1 = const_cpu_to_le32(0); - newsii.indexsz = const_cpu_to_le16(sizeof(struct SII)); - newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY)); - newsii.flags = const_cpu_to_le16(0); - newsii.fill2 = const_cpu_to_le16(0); - newsii.keysecurid = keyid; - newsii.hash = hash; - newsii.securid = keyid; - realign.all = cpu_to_le64(offs); - newsii.dataoffsh = realign.parts.dataoffsh; - newsii.dataoffsl = realign.parts.dataoffsl; - newsii.datasize = cpu_to_le32(attrsz - + sizeof(SECURITY_DESCRIPTOR_HEADER)); - if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) { - - /* enter a new $SDH record */ - - xsdh = vol->secure_xsdh; - ntfs_index_ctx_reinit(xsdh); - newsdh.offs = const_cpu_to_le16(24); - newsdh.size = const_cpu_to_le16( - sizeof(SECURITY_DESCRIPTOR_HEADER)); - newsdh.fill1 = const_cpu_to_le32(0); - newsdh.indexsz = const_cpu_to_le16( - sizeof(struct SDH)); - newsdh.indexksz = const_cpu_to_le16( - sizeof(SDH_INDEX_KEY)); - newsdh.flags = const_cpu_to_le16(0); - newsdh.fill2 = const_cpu_to_le16(0); - newsdh.keyhash = hash; - newsdh.keysecurid = keyid; - newsdh.hash = hash; - newsdh.securid = keyid; - newsdh.dataoffsh = realign.parts.dataoffsh; - newsdh.dataoffsl = realign.parts.dataoffsl; - newsdh.datasize = cpu_to_le32(attrsz - + sizeof(SECURITY_DESCRIPTOR_HEADER)); - /* special filler value, Windows generally */ - /* fills with 0x00490049, sometimes with zero */ - newsdh.fill3 = const_cpu_to_le32(0x00490049); - if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh)) - res = 0; - } - return (res); -} - -/* - * Enter a new security descriptor in $Secure (data and indexes) - * Returns id of entry, or zero if there is a problem. - * (should not be called for NTFS version < 3.0) - * - * important : calls have to be serialized, however no locking is - * needed while fuse is not multithreaded - */ - -static le32 entersecurityattr(ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz, - le32 hash) -{ - union { - struct { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - le32 securid; - le32 keyid; - u32 newkey; - off_t offs; - int gap; - int size; - BOOL found; - struct SII *psii; - INDEX_ENTRY *entry; - INDEX_ENTRY *next; - ntfs_index_context *xsii; - int retries; - ntfs_attr *na; - int olderrno; - - /* find the first available securid beyond the last key */ - /* in $Secure:$SII. This also determines the first */ - /* available location in $Secure:$SDS, as this stream */ - /* is always appended to and the id's are allocated */ - /* in sequence */ - - securid = const_cpu_to_le32(0); - xsii = vol->secure_xsii; - ntfs_index_ctx_reinit(xsii); - offs = size = 0; - keyid = const_cpu_to_le32(-1); - olderrno = errno; - found = !ntfs_index_lookup((char*)&keyid, - sizeof(SII_INDEX_KEY), xsii); - if (!found && (errno != ENOENT)) { - ntfs_log_perror("Inconsistency in index $SII"); - psii = (struct SII*)NULL; - } else { - /* restore errno to avoid misinterpretation */ - errno = olderrno; - entry = xsii->entry; - psii = (struct SII*)xsii->entry; - } - if (psii) { - /* - * Get last entry in block, but must get first one - * one first, as we should already be beyond the - * last one. For some reason the search for the last - * entry sometimes does not return the last block... - * we assume this can only happen in root block - */ - if (xsii->is_in_root) - entry = ntfs_ie_get_first - ((INDEX_HEADER*)&xsii->ir->index); - else - entry = ntfs_ie_get_first - ((INDEX_HEADER*)&xsii->ib->index); - /* - * All index blocks should be at least half full - * so there always is a last entry but one, - * except when creating the first entry in index root. - * This was however found not to be true : chkdsk - * sometimes deletes all the (unused) keys in the last - * index block without rebalancing the tree. - * When this happens, a new search is restarted from - * the smallest key. - */ - keyid = const_cpu_to_le32(0); - retries = 0; - while (entry) { - next = ntfs_index_next(entry,xsii); - if (next) { - psii = (struct SII*)next; - /* save last key and */ - /* available position */ - keyid = psii->keysecurid; - realign.parts.dataoffsh - = psii->dataoffsh; - realign.parts.dataoffsl - = psii->dataoffsl; - offs = le64_to_cpu(realign.all); - size = le32_to_cpu(psii->datasize); - } - entry = next; - if (!entry && !keyid && !retries) { - /* search failed, retry from smallest key */ - ntfs_index_ctx_reinit(xsii); - found = !ntfs_index_lookup((char*)&keyid, - sizeof(SII_INDEX_KEY), xsii); - if (!found && (errno != ENOENT)) { - ntfs_log_perror("Index $SII is broken"); - } else { - /* restore errno */ - errno = olderrno; - entry = xsii->entry; - } - retries++; - } - } - } - if (!keyid) { - /* - * could not find any entry, before creating the first - * entry, make a double check by making sure size of $SII - * is less than needed for one entry - */ - securid = const_cpu_to_le32(0); - na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4); - if (na) { - if ((size_t)na->data_size < sizeof(struct SII)) { - ntfs_log_error("Creating the first security_id\n"); - securid = const_cpu_to_le32(FIRST_SECURITY_ID); - } - ntfs_attr_close(na); - } - if (!securid) { - ntfs_log_error("Error creating a security_id\n"); - errno = EIO; - } - } else { - newkey = le32_to_cpu(keyid) + 1; - securid = cpu_to_le32(newkey); - } - /* - * The security attr has to be written twice 256KB - * apart. This implies that offsets like - * 0x40000*odd_integer must be left available for - * the second copy. So align to next block when - * the last byte overflows on a wrong block. - */ - - if (securid) { - gap = (-size) & (ALIGN_SDS_ENTRY - 1); - offs += gap + size; - if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) - & ALIGN_SDS_BLOCK) { - offs = ((offs + attrsz - + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1) - | (ALIGN_SDS_BLOCK - 1)) + 1; - } - if (!(offs & (ALIGN_SDS_BLOCK - 1))) - entersecurity_stuff(vol, offs); - /* - * now write the security attr to storage : - * first data, then SII, then SDH - * If failure occurs while writing SDS, data will never - * be accessed through indexes, and will be overwritten - * by the next allocated descriptor - * If failure occurs while writing SII, the id has not - * recorded and will be reallocated later - * If failure occurs while writing SDH, the space allocated - * in SDS or SII will not be reused, an inconsistency - * will persist with no significant consequence - */ - if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap) - || entersecurity_indexes(vol, attrsz, hash, securid, offs)) - securid = const_cpu_to_le32(0); - } - /* inode now is dirty, synchronize it all */ - ntfs_index_entry_mark_dirty(vol->secure_xsii); - ntfs_index_ctx_reinit(vol->secure_xsii); - ntfs_index_entry_mark_dirty(vol->secure_xsdh); - ntfs_index_ctx_reinit(vol->secure_xsdh); - NInoSetDirty(vol->secure_ni); - if (ntfs_inode_sync(vol->secure_ni)) - ntfs_log_perror("Could not sync $Secure\n"); - return (securid); -} - -/* - * Find a matching security descriptor in $Secure, - * if none, allocate a new id and write the descriptor to storage - * Returns id of entry, or zero if there is a problem. - * - * important : calls have to be serialized, however no locking is - * needed while fuse is not multithreaded - */ - -static le32 setsecurityattr(ntfs_volume *vol, - const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz) -{ - struct SDH *psdh; /* this is an image of index (le) */ - union { - struct { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - BOOL found; - BOOL collision; - size_t size; - size_t rdsize; - s64 offs; - int res; - ntfs_index_context *xsdh; - char *oldattr; - SDH_INDEX_KEY key; - INDEX_ENTRY *entry; - le32 securid; - le32 hash; - int olderrno; - - hash = ntfs_security_hash(attr,attrsz); - oldattr = (char*)NULL; - securid = const_cpu_to_le32(0); - res = 0; - xsdh = vol->secure_xsdh; - if (vol->secure_ni && xsdh && !vol->secure_reentry++) { - ntfs_index_ctx_reinit(xsdh); - /* - * find the nearest key as (hash,0) - * (do not search for partial key : in case of collision, - * it could return a key which is not the first one which - * collides) - */ - key.hash = hash; - key.security_id = const_cpu_to_le32(0); - olderrno = errno; - found = !ntfs_index_lookup((char*)&key, - sizeof(SDH_INDEX_KEY), xsdh); - if (!found && (errno != ENOENT)) - ntfs_log_perror("Inconsistency in index $SDH"); - else { - /* restore errno to avoid misinterpretation */ - errno = olderrno; - entry = xsdh->entry; - found = FALSE; - /* - * lookup() may return a node with no data, - * if so get next - */ - if (entry->ie_flags & INDEX_ENTRY_END) - entry = ntfs_index_next(entry,xsdh); - do { - collision = FALSE; - psdh = (struct SDH*)entry; - if (psdh) - size = (size_t) le32_to_cpu(psdh->datasize) - - sizeof(SECURITY_DESCRIPTOR_HEADER); - else size = 0; - /* if hash is not the same, the key is not present */ - if (psdh && (size > 0) - && (psdh->keyhash == hash)) { - /* if hash is the same */ - /* check the whole record */ - realign.parts.dataoffsh = psdh->dataoffsh; - realign.parts.dataoffsl = psdh->dataoffsl; - offs = le64_to_cpu(realign.all) - + sizeof(SECURITY_DESCRIPTOR_HEADER); - oldattr = (char*)ntfs_malloc(size); - if (oldattr) { - rdsize = ntfs_attr_data_read( - vol->secure_ni, - STREAM_SDS, 4, - oldattr, size, offs); - found = (rdsize == size) - && !memcmp(oldattr,attr,size); - free(oldattr); - /* if the records do not compare */ - /* (hash collision), try next one */ - if (!found) { - entry = ntfs_index_next( - entry,xsdh); - collision = TRUE; - } - } else - res = ENOMEM; - } - } while (collision && entry); - if (found) - securid = psdh->keysecurid; - else { - if (res) { - errno = res; - securid = const_cpu_to_le32(0); - } else { - /* - * no matching key : - * have to build a new one - */ - securid = entersecurityattr(vol, - attr, attrsz, hash); - } - } - } - } - if (--vol->secure_reentry) - ntfs_log_perror("Reentry error, check no multithreading\n"); - return (securid); -} - - -/* - * Update the security descriptor of a file - * Either as an attribute (complying with pre v3.x NTFS version) - * or, when possible, as an entry in $Secure (for NTFS v3.x) - * - * returns 0 if success - */ - -static int update_secur_descr(ntfs_volume *vol, - char *newattr, ntfs_inode *ni) -{ - int newattrsz; - int written; - int res; - ntfs_attr *na; - - newattrsz = ntfs_attr_size(newattr); - -#if !FORCE_FORMAT_v1x - if ((vol->major_ver < 3) || !vol->secure_ni) { -#endif - - /* update for NTFS format v1.x */ - - /* update the old security attribute */ - na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0); - if (na) { - /* resize attribute */ - res = ntfs_attr_truncate(na, (s64) newattrsz); - /* overwrite value */ - if (!res) { - written = (int)ntfs_attr_pwrite(na, (s64) 0, - (s64) newattrsz, newattr); - if (written != newattrsz) { - ntfs_log_error("Failed to update " - "a v1.x security descriptor\n"); - errno = EIO; - res = -1; - } - } - - ntfs_attr_close(na); - /* if old security attribute was found, also */ - /* truncate standard information attribute to v1.x */ - /* this is needed when security data is wanted */ - /* as v1.x though volume is formatted for v3.x */ - na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0); - if (na) { - clear_nino_flag(ni, v3_Extensions); - /* - * Truncating the record does not sweep extensions - * from copy in memory. Clear security_id to be safe - */ - ni->security_id = const_cpu_to_le32(0); - res = ntfs_attr_truncate(na, (s64)48); - ntfs_attr_close(na); - clear_nino_flag(ni, v3_Extensions); - } - } else { - /* - * insert the new security attribute if there - * were none - */ - res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0, (u8*)newattr, - (s64) newattrsz); - } -#if !FORCE_FORMAT_v1x - } else { - - /* update for NTFS format v3.x */ - - le32 securid; - - securid = setsecurityattr(vol, - (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, - (s64)newattrsz); - if (securid) { - na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0); - if (na) { - res = 0; - if (!test_nino_flag(ni, v3_Extensions)) { - /* expand standard information attribute to v3.x */ - res = ntfs_attr_truncate(na, - (s64)sizeof(STANDARD_INFORMATION)); - ni->owner_id = const_cpu_to_le32(0); - ni->quota_charged = const_cpu_to_le64(0); - ni->usn = const_cpu_to_le64(0); - ntfs_attr_remove(ni, - AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0); - } - set_nino_flag(ni, v3_Extensions); - ni->security_id = securid; - ntfs_attr_close(na); - } else { - ntfs_log_error("Failed to update " - "standard informations\n"); - errno = EIO; - res = -1; - } - } else - res = -1; - } -#endif - - /* mark node as dirty */ - NInoSetDirty(ni); - return (res); -} - -/* - * Upgrade the security descriptor of a file - * This is intended to allow graceful upgrades for files which - * were created in previous versions, with a security attributes - * and no security id. - * - * It will allocate a security id and replace the individual - * security attribute by a reference to the global one - * - * Special files are not upgraded (currently / and files in - * directories /$*) - * - * Though most code is similar to update_secur_desc() it has - * been kept apart to facilitate the further processing of - * special cases or even to remove it if found dangerous. - * - * returns 0 if success, - * 1 if not upgradable. This is not an error. - * -1 if there is a problem - */ - -static int upgrade_secur_desc(ntfs_volume *vol, - const char *attr, ntfs_inode *ni) -{ - int attrsz; - int res; - le32 securid; - ntfs_attr *na; - - /* - * upgrade requires NTFS format v3.x - * also refuse upgrading for special files - * whose number is less than FILE_first_user - */ - - if ((vol->major_ver >= 3) - && (ni->mft_no >= FILE_first_user)) { - attrsz = ntfs_attr_size(attr); - securid = setsecurityattr(vol, - (const SECURITY_DESCRIPTOR_RELATIVE*)attr, - (s64)attrsz); - if (securid) { - na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION, - AT_UNNAMED, 0); - if (na) { - /* expand standard information attribute to v3.x */ - res = ntfs_attr_truncate(na, - (s64)sizeof(STANDARD_INFORMATION)); - ni->owner_id = const_cpu_to_le32(0); - ni->quota_charged = const_cpu_to_le64(0); - ni->usn = const_cpu_to_le64(0); - ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0); - set_nino_flag(ni, v3_Extensions); - ni->security_id = securid; - ntfs_attr_close(na); - } else { - ntfs_log_error("Failed to upgrade " - "standard informations\n"); - errno = EIO; - res = -1; - } - } else - res = -1; - /* mark node as dirty */ - NInoSetDirty(ni); - } else - res = 1; - - return (res); -} - -/* - * Optional simplified checking of group membership - * - * This only takes into account the groups defined in - * /etc/group at initialization time. - * It does not take into account the groups dynamically set by - * setgroups() nor the changes in /etc/group since initialization - * - * This optional method could be useful if standard checking - * leads to a performance concern. - * - * Should not be called for user root, however the group may be root - * - */ - -static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) -{ - BOOL ingroup; - int grcnt; - gid_t *groups; - struct MAPPING *user; - - ingroup = FALSE; - if (uid) { - user = scx->mapping[MAPUSERS]; - while (user && ((uid_t)user->xid != uid)) - user = user->next; - if (user) { - groups = user->groups; - grcnt = user->grcnt; - while ((--grcnt >= 0) && (groups[grcnt] != gid)) { } - ingroup = (grcnt >= 0); - } - } - return (ingroup); -} - - -/* - * Check whether current thread owner is member of file group - * - * Should not be called for user root, however the group may be root - * - * As indicated by Miklos Szeredi : - * - * The group list is available in - * - * /proc/$PID/task/$TID/status - * - * and fuse supplies TID in get_fuse_context()->pid. The only problem is - * finding out PID, for which I have no good solution, except to iterate - * through all processes. This is rather slow, but may be speeded up - * with caching and heuristics (for single threaded programs PID = TID). - * - * The following implementation gets the group list from - * /proc/$TID/task/$TID/status which apparently exists and - * contains the same data. - */ - -static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) -{ - static char key[] = "\nGroups:"; - char buf[BUFSZ+1]; - char filename[64]; - enum { INKEY, INSEP, INNUM, INEND } state; - int fd; - char c; - int matched; - BOOL ismember; - int got; - char *p; - gid_t grp; - pid_t tid; - - if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS)) - ismember = staticgroupmember(scx, uid, gid); - else { - ismember = FALSE; /* default return */ - tid = scx->tid; - sprintf(filename,"/proc/%u/task/%u/status",tid,tid); - fd = open(filename,O_RDONLY); - if (fd >= 0) { - got = read(fd, buf, BUFSZ); - buf[got] = 0; - state = INKEY; - matched = 0; - p = buf; - grp = 0; - /* - * A simple automaton to process lines like - * Groups: 14 500 513 - */ - do { - c = *p++; - if (!c) { - /* refill buffer */ - got = read(fd, buf, BUFSZ); - buf[got] = 0; - p = buf; - c = *p++; /* 0 at end of file */ - } - switch (state) { - case INKEY : - if (key[matched] == c) { - if (!key[++matched]) - state = INSEP; - } else - if (key[0] == c) - matched = 1; - else - matched = 0; - break; - case INSEP : - if ((c >= '0') && (c <= '9')) { - grp = c - '0'; - state = INNUM; - } else - if ((c != ' ') && (c != '\t')) - state = INEND; - break; - case INNUM : - if ((c >= '0') && (c <= '9')) - grp = grp*10 + c - '0'; - else { - ismember = (grp == gid); - if ((c != ' ') && (c != '\t')) - state = INEND; - else - state = INSEP; - } - default : - break; - } - } while (!ismember && c && (state != INEND)); - close(fd); - if (!c) - ntfs_log_error("No group record found in %s\n",filename); - } else - ntfs_log_error("Could not open %s\n",filename); - } - return (ismember); -} - -/* - * Cacheing is done two-way : - * - from uid, gid and perm to securid (CACHED_SECURID) - * - from a securid to uid, gid and perm (CACHED_PERMISSIONS) - * - * CACHED_SECURID data is kept in a most-recent-first list - * which should not be too long to be efficient. Its optimal - * size is depends on usage and is hard to determine. - * - * CACHED_PERMISSIONS data is kept in a two-level indexed array. It - * is optimal at the expense of storage. Use of a most-recent-first - * list would save memory and provide similar performances for - * standard usage, but not for file servers with too many file - * owners - * - * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS - * for legacy directories which were not allocated a security_id - * it is organized in a most-recent-first list. - * - * In main caches, data is never invalidated, as the meaning of - * a security_id only changes when user mapping is changed, which - * current implies remounting. However returned entries may be - * overwritten at next update, so data has to be copied elsewhere - * before another cache update is made. - * In legacy cache, data has to be invalidated when protection is - * changed. - * - * Though the same data may be found in both list, they - * must be kept separately : the interpretation of ACL - * in both direction are approximations which could be non - * reciprocal for some configuration of the user mapping data - * - * During the process of recompiling ntfs-3g from a tgz archive, - * security processing added 7.6% to the cpu time used by ntfs-3g - * and 30% if the cache is disabled. - */ - -static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx, - u32 securindex) -{ - struct PERMISSIONS_CACHE *cache; - unsigned int index1; - unsigned int i; - - cache = (struct PERMISSIONS_CACHE*)NULL; - /* create the first permissions blocks */ - index1 = securindex >> CACHE_PERMISSIONS_BITS; - cache = (struct PERMISSIONS_CACHE*) - ntfs_malloc(sizeof(struct PERMISSIONS_CACHE) - + index1*sizeof(struct CACHED_PERMISSIONS*)); - if (cache) { - cache->head.last = index1; - cache->head.p_reads = 0; - cache->head.p_hits = 0; - cache->head.p_writes = 0; - *scx->pseccache = cache; - for (i=0; i<=index1; i++) - cache->cachetable[i] - = (struct CACHED_PERMISSIONS*)NULL; - } - return (cache); -} - -/* - * Free memory used by caches - * The only purpose is to facilitate the detection of memory leaks - */ - -static void free_caches(struct SECURITY_CONTEXT *scx) -{ - unsigned int index1; - struct PERMISSIONS_CACHE *pseccache; - - pseccache = *scx->pseccache; - if (pseccache) { - for (index1=0; index1<=pseccache->head.last; index1++) - if (pseccache->cachetable[index1]) { -#if POSIXACLS - struct CACHED_PERMISSIONS *cacheentry; - unsigned int index2; - - for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) { - cacheentry = &pseccache->cachetable[index1][index2]; - if (cacheentry->valid - && cacheentry->pxdesc) - free(cacheentry->pxdesc); - } -#endif - free(pseccache->cachetable[index1]); - } - free(pseccache); - } -} - -static int compare(const struct CACHED_SECURID *cached, - const struct CACHED_SECURID *item) -{ -#if POSIXACLS - size_t csize; - size_t isize; - - /* only compare data and sizes */ - csize = (cached->variable ? - sizeof(struct POSIX_ACL) - + (((struct POSIX_SECURITY*)cached->variable)->acccnt - + ((struct POSIX_SECURITY*)cached->variable)->defcnt) - *sizeof(struct POSIX_ACE) : - 0); - isize = (item->variable ? - sizeof(struct POSIX_ACL) - + (((struct POSIX_SECURITY*)item->variable)->acccnt - + ((struct POSIX_SECURITY*)item->variable)->defcnt) - *sizeof(struct POSIX_ACE) : - 0); - return ((cached->uid != item->uid) - || (cached->gid != item->gid) - || (cached->dmode != item->dmode) - || (csize != isize) - || (csize - && isize - && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl, - &((struct POSIX_SECURITY*)item->variable)->acl, csize))); -#else - return ((cached->uid != item->uid) - || (cached->gid != item->gid) - || (cached->dmode != item->dmode)); -#endif -} - -static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached, - const struct CACHED_PERMISSIONS_LEGACY *item) -{ - return (cached->mft_no != item->mft_no); -} - -/* - * Resize permission cache table - * do not call unless resizing is needed - * - * If allocation fails, the cache size is not updated - * Lack of memory is not considered as an error, the cache is left - * consistent and errno is not set. - */ - -static void resize_cache(struct SECURITY_CONTEXT *scx, - u32 securindex) -{ - struct PERMISSIONS_CACHE *oldcache; - struct PERMISSIONS_CACHE *newcache; - int newcnt; - int oldcnt; - unsigned int index1; - unsigned int i; - - oldcache = *scx->pseccache; - index1 = securindex >> CACHE_PERMISSIONS_BITS; - newcnt = index1 + 1; - if (newcnt <= ((CACHE_PERMISSIONS_SIZE - + (1 << CACHE_PERMISSIONS_BITS) - - 1) >> CACHE_PERMISSIONS_BITS)) { - /* expand cache beyond current end, do not use realloc() */ - /* to avoid losing data when there is no more memory */ - oldcnt = oldcache->head.last + 1; - newcache = (struct PERMISSIONS_CACHE*) - ntfs_malloc( - sizeof(struct PERMISSIONS_CACHE) - + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); - if (newcache) { - memcpy(newcache,oldcache, - sizeof(struct PERMISSIONS_CACHE) - + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*)); - free(oldcache); - /* mark new entries as not valid */ - for (i=newcache->head.last+1; i<=index1; i++) - newcache->cachetable[i] - = (struct CACHED_PERMISSIONS*)NULL; - newcache->head.last = index1; - *scx->pseccache = newcache; - } - } -} - -/* - * Enter uid, gid and mode into cache, if possible - * - * returns the updated or created cache entry, - * or NULL if not possible (typically if there is no - * security id associated) - */ - -#if POSIXACLS -static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - struct POSIX_SECURITY *pxdesc) -#else -static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode) -#endif -{ - struct CACHED_PERMISSIONS *cacheentry; - struct CACHED_PERMISSIONS *cacheblock; - struct PERMISSIONS_CACHE *pcache; - u32 securindex; -#if POSIXACLS - int pxsize; - struct POSIX_SECURITY *pxcached; -#endif - unsigned int index1; - unsigned int index2; - int i; - - /* cacheing is only possible if a security_id has been defined */ - if (test_nino_flag(ni, v3_Extensions) - && ni->security_id) { - /* - * Immediately test the most frequent situation - * where the entry exists - */ - securindex = le32_to_cpu(ni->security_id); - index1 = securindex >> CACHE_PERMISSIONS_BITS; - index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); - pcache = *scx->pseccache; - if (pcache - && (pcache->head.last >= index1) - && pcache->cachetable[index1]) { - cacheentry = &pcache->cachetable[index1][index2]; - cacheentry->uid = uid; - cacheentry->gid = gid; -#if POSIXACLS - if (cacheentry->valid && cacheentry->pxdesc) - free(cacheentry->pxdesc); - if (pxdesc) { - pxsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); - pxcached = (struct POSIX_SECURITY*)malloc(pxsize); - if (pxcached) { - memcpy(pxcached, pxdesc, pxsize); - cacheentry->pxdesc = pxcached; - } else { - cacheentry->valid = 0; - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - } - cacheentry->mode = pxdesc->mode & 07777; - } else - cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; -#else - cacheentry->mode = mode & 07777; -#endif - cacheentry->inh_fileid = const_cpu_to_le32(0); - cacheentry->inh_dirid = const_cpu_to_le32(0); - cacheentry->valid = 1; - pcache->head.p_writes++; - } else { - if (!pcache) { - /* create the first cache block */ - pcache = create_caches(scx, securindex); - } else { - if (index1 > pcache->head.last) { - resize_cache(scx, securindex); - pcache = *scx->pseccache; - } - } - /* allocate block, if cache table was allocated */ - if (pcache && (index1 <= pcache->head.last)) { - cacheblock = (struct CACHED_PERMISSIONS*) - malloc(sizeof(struct CACHED_PERMISSIONS) - << CACHE_PERMISSIONS_BITS); - pcache->cachetable[index1] = cacheblock; - for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++) - cacheblock[i].valid = 0; - cacheentry = &cacheblock[index2]; - if (cacheentry) { - cacheentry->uid = uid; - cacheentry->gid = gid; -#if POSIXACLS - if (pxdesc) { - pxsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); - pxcached = (struct POSIX_SECURITY*)malloc(pxsize); - if (pxcached) { - memcpy(pxcached, pxdesc, pxsize); - cacheentry->pxdesc = pxcached; - } else { - cacheentry->valid = 0; - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - } - cacheentry->mode = pxdesc->mode & 07777; - } else - cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL; -#else - cacheentry->mode = mode & 07777; -#endif - cacheentry->inh_fileid = const_cpu_to_le32(0); - cacheentry->inh_dirid = const_cpu_to_le32(0); - cacheentry->valid = 1; - pcache->head.p_writes++; - } - } else - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - } - } else { - cacheentry = (struct CACHED_PERMISSIONS*)NULL; -#if CACHE_LEGACY_SIZE - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { - struct CACHED_PERMISSIONS_LEGACY wanted; - struct CACHED_PERMISSIONS_LEGACY *legacy; - - wanted.perm.uid = uid; - wanted.perm.gid = gid; -#if POSIXACLS - wanted.perm.mode = pxdesc->mode & 07777; - wanted.perm.inh_fileid = const_cpu_to_le32(0); - wanted.perm.inh_dirid = const_cpu_to_le32(0); - wanted.mft_no = ni->mft_no; - wanted.variable = (void*)pxdesc; - wanted.varsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); -#else - wanted.perm.mode = mode & 07777; - wanted.perm.inh_fileid = const_cpu_to_le32(0); - wanted.perm.inh_dirid = const_cpu_to_le32(0); - wanted.mft_no = ni->mft_no; - wanted.variable = (void*)NULL; - wanted.varsize = 0; -#endif - legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache( - scx->vol->legacy_cache, GENERIC(&wanted), - (cache_compare)leg_compare); - if (legacy) { - cacheentry = &legacy->perm; -#if POSIXACLS - /* - * give direct access to the cached pxdesc - * in the permissions structure - */ - cacheentry->pxdesc = legacy->variable; -#endif - } - } -#endif - } - return (cacheentry); -} - -/* - * Fetch owner, group and permission of a file, if cached - * - * Beware : do not use the returned entry after a cache update : - * the cache may be relocated making the returned entry meaningless - * - * returns the cache entry, or NULL if not available - */ - -static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni) -{ - struct CACHED_PERMISSIONS *cacheentry; - struct PERMISSIONS_CACHE *pcache; - u32 securindex; - unsigned int index1; - unsigned int index2; - - /* cacheing is only possible if a security_id has been defined */ - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - if (test_nino_flag(ni, v3_Extensions) - && (ni->security_id)) { - securindex = le32_to_cpu(ni->security_id); - index1 = securindex >> CACHE_PERMISSIONS_BITS; - index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1); - pcache = *scx->pseccache; - if (pcache - && (pcache->head.last >= index1) - && pcache->cachetable[index1]) { - cacheentry = &pcache->cachetable[index1][index2]; - /* reject if entry is not valid */ - if (!cacheentry->valid) - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - else - pcache->head.p_hits++; - if (pcache) - pcache->head.p_reads++; - } - } -#if CACHE_LEGACY_SIZE - else { - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { - struct CACHED_PERMISSIONS_LEGACY wanted; - struct CACHED_PERMISSIONS_LEGACY *legacy; - - wanted.mft_no = ni->mft_no; - wanted.variable = (void*)NULL; - wanted.varsize = 0; - legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache( - scx->vol->legacy_cache, GENERIC(&wanted), - (cache_compare)leg_compare); - if (legacy) cacheentry = &legacy->perm; - } - } -#endif -#if POSIXACLS - if (cacheentry && !cacheentry->pxdesc) { - ntfs_log_error("No Posix descriptor in cache\n"); - cacheentry = (struct CACHED_PERMISSIONS*)NULL; - } -#endif - return (cacheentry); -} - -/* - * Retrieve a security attribute from $Secure - */ - -static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) -{ - struct SII *psii; - union { - struct { - le32 dataoffsl; - le32 dataoffsh; - } parts; - le64 all; - } realign; - int found; - size_t size; - size_t rdsize; - s64 offs; - ntfs_inode *ni; - ntfs_index_context *xsii; - char *securattr; - - securattr = (char*)NULL; - ni = vol->secure_ni; - xsii = vol->secure_xsii; - if (ni && xsii) { - ntfs_index_ctx_reinit(xsii); - found = - !ntfs_index_lookup((char*)&id, - sizeof(SII_INDEX_KEY), xsii); - if (found) { - psii = (struct SII*)xsii->entry; - size = - (size_t) le32_to_cpu(psii->datasize) - - sizeof(SECURITY_DESCRIPTOR_HEADER); - /* work around bad alignment problem */ - realign.parts.dataoffsh = psii->dataoffsh; - realign.parts.dataoffsl = psii->dataoffsl; - offs = le64_to_cpu(realign.all) - + sizeof(SECURITY_DESCRIPTOR_HEADER); - - securattr = (char*)ntfs_malloc(size); - if (securattr) { - rdsize = ntfs_attr_data_read( - ni, STREAM_SDS, 4, - securattr, size, offs); - if ((rdsize != size) - || !ntfs_valid_descr(securattr, - rdsize)) { - /* error to be logged by caller */ - free(securattr); - securattr = (char*)NULL; - } - } - } else - if (errno != ENOENT) - ntfs_log_perror("Inconsistency in index $SII"); - } - if (!securattr) { - ntfs_log_error("Failed to retrieve a security descriptor\n"); - errno = EIO; - } - return (securattr); -} - -/* - * Get the security descriptor associated to a file - * - * Either : - * - read the security descriptor attribute (v1.x format) - * - or find the descriptor in $Secure:$SDS (v3.x format) - * - * in both case, sanity checks are done on the attribute and - * the descriptor can be assumed safe - * - * The returned descriptor is dynamically allocated and has to be freed - */ - -static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) -{ - SII_INDEX_KEY securid; - char *securattr; - s64 readallsz; - - /* - * Warning : in some situations, after fixing by chkdsk, - * v3_Extensions are marked present (long standard informations) - * with a default security descriptor inserted in an - * attribute - */ - if (test_nino_flag(ni, v3_Extensions) - && vol->secure_ni && ni->security_id) { - /* get v3.x descriptor in $Secure */ - securid.security_id = ni->security_id; - securattr = retrievesecurityattr(vol,securid); - if (!securattr) - ntfs_log_error("Bad security descriptor for 0x%lx\n", - (long)le32_to_cpu(ni->security_id)); - } else { - /* get v1.x security attribute */ - readallsz = 0; - securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, - AT_UNNAMED, 0, &readallsz); - if (securattr && !ntfs_valid_descr(securattr, readallsz)) { - ntfs_log_error("Bad security descriptor for inode %lld\n", - (long long)ni->mft_no); - free(securattr); - securattr = (char*)NULL; - } - } - if (!securattr) { - /* - * in some situations, there is no security - * descriptor, and chkdsk does not detect or fix - * anything. This could be a normal situation. - * When this happens, simulate a descriptor with - * minimum rights, so that a real descriptor can - * be created by chown or chmod - */ - ntfs_log_error("No security descriptor found for inode %lld\n", - (long long)ni->mft_no); - securattr = ntfs_build_descr(0, 0, adminsid, adminsid); - } - return (securattr); -} - -#if POSIXACLS - -/* - * Determine which access types to a file are allowed - * according to the relation of current process to the file - * - * Do not call if default_permissions is set - */ - -static int access_check_posix(struct SECURITY_CONTEXT *scx, - struct POSIX_SECURITY *pxdesc, mode_t request, - uid_t uid, gid_t gid) -{ - struct POSIX_ACE *pxace; - int userperms; - int groupperms; - int mask; - BOOL somegroup; - BOOL needgroups; - mode_t perms; - int i; - - perms = pxdesc->mode; - /* owner and root access */ - if (!scx->uid || (uid == scx->uid)) { - if (!scx->uid) { - /* root access if owner or other execution */ - if (perms & 0101) - perms = 07777; - else { - /* root access if some group execution */ - groupperms = 0; - mask = 7; - for (i=pxdesc->acccnt-1; i>=0 ; i--) { - pxace = &pxdesc->acl.ace[i]; - switch (pxace->tag) { - case POSIX_ACL_USER_OBJ : - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_GROUP : - groupperms |= pxace->perms; - break; - case POSIX_ACL_MASK : - mask = pxace->perms & 7; - break; - default : - break; - } - } - perms = (groupperms & mask & 1) | 6; - } - } else - perms &= 07700; - } else { - /* - * analyze designated users, get mask - * and identify whether we need to check - * the group memberships. The groups are - * not needed when all groups have the - * same permissions as other for the - * requested modes. - */ - userperms = -1; - groupperms = -1; - needgroups = FALSE; - mask = 7; - for (i=pxdesc->acccnt-1; i>=0 ; i--) { - pxace = &pxdesc->acl.ace[i]; - switch (pxace->tag) { - case POSIX_ACL_USER : - if ((uid_t)pxace->id == scx->uid) - userperms = pxace->perms; - break; - case POSIX_ACL_MASK : - mask = pxace->perms & 7; - break; - case POSIX_ACL_GROUP_OBJ : - case POSIX_ACL_GROUP : - if (((pxace->perms & mask) ^ perms) - & (request >> 6) & 7) - needgroups = TRUE; - break; - default : - break; - } - } - /* designated users */ - if (userperms >= 0) - perms = (perms & 07000) + (userperms & mask); - else if (!needgroups) - perms &= 07007; - else { - /* owning group */ - if (!(~(perms >> 3) & request & mask) - && ((gid == scx->gid) - || groupmember(scx, scx->uid, gid))) - perms &= 07070; - else { - /* other groups */ - groupperms = -1; - somegroup = FALSE; - for (i=pxdesc->acccnt-1; i>=0 ; i--) { - pxace = &pxdesc->acl.ace[i]; - if ((pxace->tag == POSIX_ACL_GROUP) - && groupmember(scx, uid, pxace->id)) { - if (!(~pxace->perms & request & mask)) - groupperms = pxace->perms; - somegroup = TRUE; - } - } - if (groupperms >= 0) - perms = (perms & 07000) + (groupperms & mask); - else - if (somegroup) - perms = 0; - else - perms &= 07007; - } - } - } - return (perms); -} - -/* - * Get permissions to access a file - * Takes into account the relation of user to file (owner, group, ...) - * Do no use as mode of the file - * Do no call if default_permissions is set - * - * returns -1 if there is a problem - */ - -static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - ntfs_inode * ni, mode_t request) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - uid_t uid; - gid_t gid; - int perm; - BOOL isdir; - struct POSIX_SECURITY *pxdesc; - - if (!scx->mapping[MAPUSERS]) - perm = 07777; - else { - /* check whether available in cache */ - cached = fetch_cache(scx,ni); - if (cached) { - uid = cached->uid; - gid = cached->gid; - perm = access_check_posix(scx,cached->pxdesc,request,uid,gid); - } else { - perm = 0; /* default to no permission */ - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, ni); - if (securattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - securattr; - gsid = (const SID*)& - securattr[le32_to_cpu(phead->group)]; - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); -#if OWNERFROMACL - usid = ntfs_acl_owner(securattr); - pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, - usid, gsid, isdir); - if (pxdesc) - perm = pxdesc->mode & 07777; - else - perm = -1; - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#else - usid = (const SID*)& - securattr[le32_to_cpu(phead->owner)]; - pxdesc = ntfs_build_permissions_posix(scx,securattr, - usid, gsid, isdir); - if (pxdesc) - perm = pxdesc->mode & 07777; - else - perm = -1; - if (!perm && ntfs_same_sid(usid, adminsid)) { - uid = find_tenant(scx, securattr); - if (uid) - perm = 0700; - } else - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#endif - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if (!test_nino_flag(ni, v3_Extensions) - && (perm >= 0) - && (scx->vol->secure_flags - & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, - securattr, ni); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if (test_nino_flag(ni, v3_Extensions) - && (perm >= 0)) { - enter_cache(scx, ni, uid, - gid, pxdesc); - } - if (pxdesc) { - perm = access_check_posix(scx,pxdesc,request,uid,gid); - free(pxdesc); - } - free(securattr); - } else { - perm = -1; - uid = gid = 0; - } - } - } - return (perm); -} - -/* - * Get a Posix ACL - * - * returns size or -errno if there is a problem - * if size was too small, no copy is done and errno is not set, - * the caller is expected to issue a new call - */ - -int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, char *value, size_t size) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - struct POSIX_SECURITY *pxdesc; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - uid_t uid; - gid_t gid; - BOOL isdir; - size_t outsize; - - outsize = 0; /* default to error */ - if (!scx->mapping[MAPUSERS]) - errno = ENOTSUP; - else { - /* check whether available in cache */ - cached = fetch_cache(scx,ni); - if (cached) - pxdesc = cached->pxdesc; - else { - securattr = getsecurityattr(scx->vol, ni); - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - if (securattr) { - phead = - (const SECURITY_DESCRIPTOR_RELATIVE*) - securattr; - gsid = (const SID*)& - securattr[le32_to_cpu(phead->group)]; -#if OWNERFROMACL - usid = ntfs_acl_owner(securattr); -#else - usid = (const SID*)& - securattr[le32_to_cpu(phead->owner)]; -#endif - pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, - usid, gsid, isdir); - - /* - * fetch owner and group for cacheing - */ - if (pxdesc) { - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if (!test_nino_flag(ni, v3_Extensions) - && (scx->vol->secure_flags - & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, - securattr, ni); - } -#if OWNERFROMACL - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#else - if (!(pxdesc->mode & 07777) - && ntfs_same_sid(usid, adminsid)) { - uid = find_tenant(scx, - securattr); - } else - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#endif - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS) - enter_cache(scx, ni, uid, - gid, pxdesc); - } - free(securattr); - } else - pxdesc = (struct POSIX_SECURITY*)NULL; - } - - if (pxdesc) { - if (ntfs_valid_posix(pxdesc)) { - if (!strcmp(name,"system.posix_acl_default")) { - if (ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY) - outsize = sizeof(struct POSIX_ACL) - + pxdesc->defcnt*sizeof(struct POSIX_ACE); - else { - /* - * getting default ACL from plain file : - * return EACCES if size > 0 as - * indicated in the man, but return ok - * if size == 0, so that ls does not - * display an error - */ - if (size > 0) { - outsize = 0; - errno = EACCES; - } else - outsize = sizeof(struct POSIX_ACL); - } - if (outsize && (outsize <= size)) { - memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL)); - memcpy(&value[sizeof(struct POSIX_ACL)], - &pxdesc->acl.ace[pxdesc->firstdef], - outsize-sizeof(struct POSIX_ACL)); - } - } else { - outsize = sizeof(struct POSIX_ACL) - + pxdesc->acccnt*sizeof(struct POSIX_ACE); - if (outsize <= size) - memcpy(value,&pxdesc->acl,outsize); - } - } else { - outsize = 0; - errno = EIO; - ntfs_log_error("Invalid Posix ACL built\n"); - } - if (!cached) - free(pxdesc); - } else - outsize = 0; - } - return (outsize ? (int)outsize : -errno); -} - -#else /* POSIXACLS */ - - -/* - * Get permissions to access a file - * Takes into account the relation of user to file (owner, group, ...) - * Do no use as mode of the file - * - * returns -1 if there is a problem - */ - -static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, mode_t request) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - BOOL isdir; - uid_t uid; - gid_t gid; - int perm; - - if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) - perm = 07777; - else { - /* check whether available in cache */ - cached = fetch_cache(scx,ni); - if (cached) { - perm = cached->mode; - uid = cached->uid; - gid = cached->gid; - } else { - perm = 0; /* default to no permission */ - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, ni); - if (securattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - securattr; - gsid = (const SID*)& - securattr[le32_to_cpu(phead->group)]; - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); -#if OWNERFROMACL - usid = ntfs_acl_owner(securattr); - perm = ntfs_build_permissions(securattr, - usid, gsid, isdir); - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#else - usid = (const SID*)& - securattr[le32_to_cpu(phead->owner)]; - perm = ntfs_build_permissions(securattr, - usid, gsid, isdir); - if (!perm && ntfs_same_sid(usid, adminsid)) { - uid = find_tenant(scx, securattr); - if (uid) - perm = 0700; - } else - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#endif - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if (!test_nino_flag(ni, v3_Extensions) - && (perm >= 0) - && (scx->vol->secure_flags - & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, - securattr, ni); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if (test_nino_flag(ni, v3_Extensions) - && (perm >= 0)) { - enter_cache(scx, ni, uid, - gid, perm); - } - free(securattr); - } else { - perm = -1; - uid = gid = 0; - } - } - if (perm >= 0) { - if (!scx->uid) { - /* root access and execution */ - if (perm & 0111) - perm = 07777; - else - perm = 0; - } else - if (uid == scx->uid) - perm &= 07700; - else - /* - * avoid checking group membership - * when the requested perms for group - * are the same as perms for other - */ - if ((gid == scx->gid) - || ((((perm >> 3) ^ perm) - & (request >> 6) & 7) - && groupmember(scx, scx->uid, gid))) - perm &= 07070; - else - perm &= 07007; - } - } - return (perm); -} - -#endif /* POSIXACLS */ - -/* - * Get an NTFS ACL - * - * Returns size or -errno if there is a problem - * if size was too small, no copy is done and errno is not set, - * the caller is expected to issue a new call - */ - -int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - char *value, size_t size) -{ - char *securattr; - size_t outsize; - - outsize = 0; /* default to no data and no error */ - securattr = getsecurityattr(scx->vol, ni); - if (securattr) { - outsize = ntfs_attr_size(securattr); - if (outsize <= size) { - memcpy(value,securattr,outsize); - } - free(securattr); - } - return (outsize ? (int)outsize : -errno); -} - -/* - * Get owner, group and permissions in an stat structure - * returns permissions, or -1 if there is a problem - */ - -int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, - ntfs_inode * ni, struct stat *stbuf) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - char *securattr; - const SID *usid; /* owner of file/directory */ - const SID *gsid; /* group of file/directory */ - const struct CACHED_PERMISSIONS *cached; - int perm; - BOOL isdir; -#if POSIXACLS - struct POSIX_SECURITY *pxdesc; -#endif - - if (!scx->mapping[MAPUSERS]) - perm = 07777; - else { - /* check whether available in cache */ - cached = fetch_cache(scx,ni); - if (cached) { - perm = cached->mode; - stbuf->st_uid = cached->uid; - stbuf->st_gid = cached->gid; - stbuf->st_mode = (stbuf->st_mode & ~07777) + perm; - } else { - perm = -1; /* default to error */ - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, ni); - if (securattr) { - phead = - (const SECURITY_DESCRIPTOR_RELATIVE*) - securattr; - gsid = (const SID*)& - securattr[le32_to_cpu(phead->group)]; -#if OWNERFROMACL - usid = ntfs_acl_owner(securattr); -#else - usid = (const SID*)& - securattr[le32_to_cpu(phead->owner)]; -#endif -#if POSIXACLS - pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr, - usid, gsid, isdir); - if (pxdesc) - perm = pxdesc->mode & 07777; - else - perm = -1; -#else - perm = ntfs_build_permissions(securattr, - usid, gsid, isdir); -#endif - /* - * fetch owner and group for cacheing - */ - if (perm >= 0) { - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if (!test_nino_flag(ni, v3_Extensions) - && (scx->vol->secure_flags - & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, - securattr, ni); - } -#if OWNERFROMACL - stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#else - if (!perm && ntfs_same_sid(usid, adminsid)) { - stbuf->st_uid = - find_tenant(scx, - securattr); - if (stbuf->st_uid) - perm = 0700; - } else - stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#endif - stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - stbuf->st_mode = - (stbuf->st_mode & ~07777) + perm; -#if POSIXACLS - enter_cache(scx, ni, stbuf->st_uid, - stbuf->st_gid, pxdesc); - free(pxdesc); -#else - enter_cache(scx, ni, stbuf->st_uid, - stbuf->st_gid, perm); -#endif - } - free(securattr); - } - } - } - return (perm); -} - -#if POSIXACLS - -/* - * Get the base for a Posix inheritance and - * build an inherited Posix descriptor - */ - -static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir) -{ - const struct CACHED_PERMISSIONS *cached; - const SECURITY_DESCRIPTOR_RELATIVE *phead; - struct POSIX_SECURITY *pxdesc; - struct POSIX_SECURITY *pydesc; - char *securattr; - const SID *usid; - const SID *gsid; - uid_t uid; - gid_t gid; - - pydesc = (struct POSIX_SECURITY*)NULL; - /* check whether parent directory is available in cache */ - cached = fetch_cache(scx,dir_ni); - if (cached) { - uid = cached->uid; - gid = cached->gid; - pxdesc = cached->pxdesc; - if (pxdesc) { - pydesc = ntfs_build_inherited_posix(pxdesc,mode, - scx->umask,isdir); - } - } else { - securattr = getsecurityattr(scx->vol, dir_ni); - if (securattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - securattr; - gsid = (const SID*)& - securattr[le32_to_cpu(phead->group)]; - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); -#if OWNERFROMACL - usid = ntfs_acl_owner(securattr); - pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, - usid, gsid, TRUE); - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#else - usid = (const SID*)& - securattr[le32_to_cpu(phead->owner)]; - pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr, - usid, gsid, TRUE); - if (pxdesc && ntfs_same_sid(usid, adminsid)) { - uid = find_tenant(scx, securattr); - } else - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); -#endif - if (pxdesc) { - /* - * Create a security id if there were none - * and upgrade option is selected - */ - if (!test_nino_flag(dir_ni, v3_Extensions) - && (scx->vol->secure_flags - & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, - securattr, dir_ni); - /* - * fetch owner and group for cacheing - * if there is a securid - */ - } - if (test_nino_flag(dir_ni, v3_Extensions)) { - enter_cache(scx, dir_ni, uid, - gid, pxdesc); - } - pydesc = ntfs_build_inherited_posix(pxdesc, - mode, scx->umask, isdir); - free(pxdesc); - } - free(securattr); - } - } - return (pydesc); -} - -/* - * Allocate a security_id for a file being created - * - * Returns zero if not possible (NTFS v3.x required) - */ - -le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, ntfs_inode *dir_ni, - mode_t mode, BOOL isdir) -{ -#if !FORCE_FORMAT_v1x - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - struct POSIX_SECURITY *pxdesc; - char *newattr; - int newattrsz; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - le32 securid; -#endif - - securid = const_cpu_to_le32(0); - -#if !FORCE_FORMAT_v1x - - pxdesc = inherit_posix(scx, dir_ni, mode, isdir); - if (pxdesc) { - /* check whether target securid is known in cache */ - - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = pxdesc->mode & mode & 07777; - if (isdir) wanted.dmode |= 0x10000; - wanted.variable = (void*)pxdesc; - wanted.varsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); - cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC(&wanted), - (cache_compare)compare); - /* quite simple, if we are lucky */ - if (cached) - securid = cached->securid; - - /* not in cache : make sure we can create ids */ - - if (!cached && (scx->vol->major_ver >= 3)) { - usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); - gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); - if (!usid || !gsid) { - ntfs_log_error("File created by an unmapped user/group %d/%d\n", - (int)uid, (int)gid); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, - isdir, usid, gsid); - if (newattr) { - newattrsz = ntfs_attr_size(newattr); - securid = setsecurityattr(scx->vol, - (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, - newattrsz); - if (securid) { - /* update cache, for subsequent use */ - wanted.securid = securid; - ntfs_enter_cache(scx->vol->securid_cache, - GENERIC(&wanted), - (cache_compare)compare); - } - free(newattr); - } else { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } - free(pxdesc); - } -#endif - return (securid); -} - -/* - * Apply Posix inheritance to a newly created file - * (for NTFS 1.x only : no securid) - */ - -int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, uid_t uid, gid_t gid, - ntfs_inode *dir_ni, mode_t mode) -{ - struct POSIX_SECURITY *pxdesc; - char *newattr; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - BOOL isdir; - int res; - - res = -1; - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - pxdesc = inherit_posix(scx, dir_ni, mode, isdir); - if (pxdesc) { - usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); - gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); - if (!usid || !gsid) { - ntfs_log_error("File created by an unmapped user/group %d/%d\n", - (int)uid, (int)gid); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, - isdir, usid, gsid); - if (newattr) { - /* Adjust Windows read-only flag */ - res = update_secur_descr(scx->vol, newattr, ni); - if (!res && !isdir) { - if (mode & S_IWUSR) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; - } -#if CACHE_LEGACY_SIZE - /* also invalidate legacy cache */ - if (isdir && !ni->security_id) { - struct CACHED_PERMISSIONS_LEGACY legacy; - - legacy.mft_no = ni->mft_no; - legacy.variable = pxdesc; - legacy.varsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); - ntfs_invalidate_cache(scx->vol->legacy_cache, - GENERIC(&legacy), - (cache_compare)leg_compare,0); - } -#endif - free(newattr); - - } else { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } - return (res); -} - -#else - -le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, mode_t mode, BOOL isdir) -{ -#if !FORCE_FORMAT_v1x - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - char *newattr; - int newattrsz; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - le32 securid; -#endif - - securid = const_cpu_to_le32(0); - -#if !FORCE_FORMAT_v1x - /* check whether target securid is known in cache */ - - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = mode & 07777; - if (isdir) wanted.dmode |= 0x10000; - wanted.variable = (void*)NULL; - wanted.varsize = 0; - cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC(&wanted), - (cache_compare)compare); - /* quite simple, if we are lucky */ - if (cached) - securid = cached->securid; - - /* not in cache : make sure we can create ids */ - - if (!cached && (scx->vol->major_ver >= 3)) { - usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); - gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); - if (!usid || !gsid) { - ntfs_log_error("File created by an unmapped user/group %d/%d\n", - (int)uid, (int)gid); - usid = gsid = adminsid; - } - newattr = ntfs_build_descr(mode, isdir, usid, gsid); - if (newattr) { - newattrsz = ntfs_attr_size(newattr); - securid = setsecurityattr(scx->vol, - (const SECURITY_DESCRIPTOR_RELATIVE*)newattr, - newattrsz); - if (securid) { - /* update cache, for subsequent use */ - wanted.securid = securid; - ntfs_enter_cache(scx->vol->securid_cache, - GENERIC(&wanted), - (cache_compare)compare); - } - free(newattr); - } else { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - } - } -#endif - return (securid); -} - -#endif - -/* - * Update ownership and mode of a file, reusing an existing - * security descriptor when possible - * - * Returns zero if successful - */ - -#if POSIXACLS -int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, mode_t mode, - struct POSIX_SECURITY *pxdesc) -#else -int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, mode_t mode) -#endif -{ - int res; - const struct CACHED_SECURID *cached; - struct CACHED_SECURID wanted; - char *newattr; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - BOOL isdir; - - res = 0; - - /* check whether target securid is known in cache */ - - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - wanted.uid = uid; - wanted.gid = gid; - wanted.dmode = mode & 07777; - if (isdir) wanted.dmode |= 0x10000; -#if POSIXACLS - wanted.variable = (void*)pxdesc; - if (pxdesc) - wanted.varsize = sizeof(struct POSIX_SECURITY) - + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); - else - wanted.varsize = 0; -#else - wanted.variable = (void*)NULL; - wanted.varsize = 0; -#endif - if (test_nino_flag(ni, v3_Extensions)) { - cached = (const struct CACHED_SECURID*)ntfs_fetch_cache( - scx->vol->securid_cache, GENERIC(&wanted), - (cache_compare)compare); - /* quite simple, if we are lucky */ - if (cached) { - ni->security_id = cached->securid; - NInoSetDirty(ni); - } - } else cached = (struct CACHED_SECURID*)NULL; - - if (!cached) { - /* - * Do not use usid and gsid from former attributes, - * but recompute them to get repeatable results - * which can be kept in cache. - */ - usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); - gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); - if (!usid || !gsid) { - ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", - uid, gid); - usid = gsid = adminsid; - } -#if POSIXACLS - if (pxdesc) - newattr = ntfs_build_descr_posix(scx->mapping, pxdesc, - isdir, usid, gsid); - else - newattr = ntfs_build_descr(mode, - isdir, usid, gsid); -#else - newattr = ntfs_build_descr(mode, - isdir, usid, gsid); -#endif - if (newattr) { - res = update_secur_descr(scx->vol, newattr, ni); - if (!res) { - /* adjust Windows read-only flag */ - if (!isdir) { - if (mode & S_IWUSR) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; - NInoFileNameSetDirty(ni); - } - /* update cache, for subsequent use */ - if (test_nino_flag(ni, v3_Extensions)) { - wanted.securid = ni->security_id; - ntfs_enter_cache(scx->vol->securid_cache, - GENERIC(&wanted), - (cache_compare)compare); - } -#if CACHE_LEGACY_SIZE - /* also invalidate legacy cache */ - if (isdir && !ni->security_id) { - struct CACHED_PERMISSIONS_LEGACY legacy; - - legacy.mft_no = ni->mft_no; -#if POSIXACLS - legacy.variable = wanted.variable; - legacy.varsize = wanted.varsize; -#else - legacy.variable = (void*)NULL; - legacy.varsize = 0; -#endif - ntfs_invalidate_cache(scx->vol->legacy_cache, - GENERIC(&legacy), - (cache_compare)leg_compare,0); - } -#endif - } - free(newattr); - } else { - /* - * could not build new security attribute - * errno set by ntfs_build_descr() - */ - res = -1; - } - } - return (res); -} - -/* - * Check whether user has ownership rights on a file - * - * Returns TRUE if allowed - * if not, errno tells why - */ - -BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) -{ - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - uid_t processuid; - uid_t uid; - BOOL gotowner; - int allowed; - - processuid = scx->uid; -/* TODO : use CAP_FOWNER process capability */ - /* - * Always allow for root - * Also always allow if no mapping has been defined - */ - if (!scx->mapping[MAPUSERS] || !processuid) - allowed = TRUE; - else { - gotowner = FALSE; /* default */ - /* get the owner, either from cache or from old attribute */ - cached = fetch_cache(scx, ni); - if (cached) { - uid = cached->uid; - gotowner = TRUE; - } else { - oldattr = getsecurityattr(scx->vol, ni); - if (oldattr) { -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - const SECURITY_DESCRIPTOR_RELATIVE *phead; - - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - oldattr; - usid = (const SID*)&oldattr - [le32_to_cpu(phead->owner)]; -#endif - uid = ntfs_find_user(scx->mapping[MAPUSERS], - usid); - gotowner = TRUE; - free(oldattr); - } - } - allowed = FALSE; - if (gotowner) { -/* TODO : use CAP_FOWNER process capability */ - if (!processuid || (processuid == uid)) - allowed = TRUE; - else - errno = EPERM; - } - } - return (allowed); -} - -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - -#if POSIXACLS - -/* - * Set a new access or default Posix ACL to a file - * (or remove ACL if no input data) - * Validity of input data is checked after merging - * - * Returns 0, or -1 if there is a problem which errno describes - */ - -int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name, const char *value, size_t size, - int flags) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - uid_t processuid; - const SID *usid; - const SID *gsid; - uid_t uid; - uid_t gid; - int res; - BOOL isdir; - BOOL deflt; - BOOL exist; - int count; - struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc; - - /* get the current pxsec, either from cache or from old attribute */ - res = -1; - deflt = !strcmp(name,"system.posix_acl_default"); - if (size) - count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE); - else - count = 0; - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - newpxdesc = (struct POSIX_SECURITY*)NULL; - if ((!value - || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION)) - && (!deflt || isdir || (!size && !value))) { - cached = fetch_cache(scx, ni); - if (cached) { - uid = cached->uid; - gid = cached->gid; - oldpxdesc = cached->pxdesc; - if (oldpxdesc) { - newpxdesc = ntfs_replace_acl(oldpxdesc, - (const struct POSIX_ACL*)value,count,deflt); - } - } else { - oldattr = getsecurityattr(scx->vol, ni); - if (oldattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; -#endif - gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - oldpxdesc = ntfs_build_permissions_posix(scx->mapping, - oldattr, usid, gsid, isdir); - if (oldpxdesc) { - if (deflt) - exist = oldpxdesc->defcnt > 0; - else - exist = oldpxdesc->acccnt > 3; - if ((exist && (flags & XATTR_CREATE)) - || (!exist && (flags & XATTR_REPLACE))) { - errno = (exist ? EEXIST : ENODATA); - } else { - newpxdesc = ntfs_replace_acl(oldpxdesc, - (const struct POSIX_ACL*)value,count,deflt); - } - free(oldpxdesc); - } - free(oldattr); - } - } - } else - errno = EINVAL; - - if (newpxdesc) { - processuid = scx->uid; -/* TODO : use CAP_FOWNER process capability */ - if (!processuid || (uid == processuid)) { - /* - * clear setgid if file group does - * not match process group - */ - if (processuid && (gid != scx->gid) - && !groupmember(scx, scx->uid, gid)) { - newpxdesc->mode &= ~S_ISGID; - } - res = ntfs_set_owner_mode(scx, ni, uid, gid, - newpxdesc->mode, newpxdesc); - } else - errno = EPERM; - free(newpxdesc); - } - return (res ? -1 : 0); -} - -/* - * Remove a default Posix ACL from a file - * - * Returns 0, or -1 if there is a problem which errno describes - */ - -int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *name) -{ - return (ntfs_set_posix_acl(scx, ni, name, - (const char*)NULL, 0, 0)); -} - -#endif - -/* - * Set a new NTFS ACL to a file - * - * Returns 0, or -1 if there is a problem - */ - -int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - const char *value, size_t size, int flags) -{ - char *attr; - int res; - - res = -1; - if ((size > 0) - && !(flags & XATTR_CREATE) - && ntfs_valid_descr(value,size) - && (ntfs_attr_size(value) == size)) { - /* need copying in order to write */ - attr = (char*)ntfs_malloc(size); - if (attr) { - memcpy(attr,value,size); - res = update_secur_descr(scx->vol, attr, ni); - /* - * No need to invalidate standard caches : - * the relation between a securid and - * the associated protection is unchanged, - * only the relation between a file and - * its securid and protection is changed. - */ -#if CACHE_LEGACY_SIZE - /* - * we must however invalidate the legacy - * cache, which is based on inode numbers. - * For safety, invalidate even if updating - * failed. - */ - if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - && !ni->security_id) { - struct CACHED_PERMISSIONS_LEGACY legacy; - - legacy.mft_no = ni->mft_no; - legacy.variable = (char*)NULL; - legacy.varsize = 0; - ntfs_invalidate_cache(scx->vol->legacy_cache, - GENERIC(&legacy), - (cache_compare)leg_compare,0); - } -#endif - free(attr); - } else - errno = ENOMEM; - } else - errno = EINVAL; - return (res ? -1 : 0); -} - -#endif /* HAVE_SETXATTR */ - -/* - * Set new permissions to a file - * Checks user mapping has been defined before request for setting - * - * rejected if request is not originated by owner or root - * - * returns 0 on success - * -1 on failure, with errno = EIO - */ - -int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - const SID *gsid; - uid_t processuid; - uid_t uid; - uid_t gid; - int res; -#if POSIXACLS - BOOL isdir; - int pxsize; - const struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; -#endif - - /* get the current owner, either from cache or from old attribute */ - res = 0; - cached = fetch_cache(scx, ni); - if (cached) { - uid = cached->uid; - gid = cached->gid; -#if POSIXACLS - oldpxdesc = cached->pxdesc; - if (oldpxdesc) { - /* must copy before merging */ - pxsize = sizeof(struct POSIX_SECURITY) - + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); - newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); - if (newpxdesc) { - memcpy(newpxdesc, oldpxdesc, pxsize); - if (ntfs_merge_mode_posix(newpxdesc, mode)) - res = -1; - } else - res = -1; - } else - newpxdesc = (struct POSIX_SECURITY*)NULL; -#endif - } else { - oldattr = getsecurityattr(scx->vol, ni); - if (oldattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; -#endif - gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)]; - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); -#if POSIXACLS - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - newpxdesc = ntfs_build_permissions_posix(scx->mapping, - oldattr, usid, gsid, isdir); - if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) - res = -1; -#endif - free(oldattr); - } else - res = -1; - } - - if (!res) { - processuid = scx->uid; -/* TODO : use CAP_FOWNER process capability */ - if (!processuid || (uid == processuid)) { - /* - * clear setgid if file group does - * not match process group - */ - if (processuid && (gid != scx->gid) - && !groupmember(scx, scx->uid, gid)) - mode &= ~S_ISGID; -#if POSIXACLS - if (newpxdesc) { - newpxdesc->mode = mode; - res = ntfs_set_owner_mode(scx, ni, uid, gid, - mode, newpxdesc); - } else - res = ntfs_set_owner_mode(scx, ni, uid, gid, - mode, newpxdesc); -#else - res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); -#endif - } else { - errno = EPERM; - res = -1; /* neither owner nor root */ - } - } else { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error("File has no security descriptor\n"); - res = -1; - errno = EIO; - } -#if POSIXACLS - if (newpxdesc) free(newpxdesc); -#endif - return (res ? -1 : 0); -} - -/* - * Create a default security descriptor for files whose descriptor - * cannot be inherited - */ - -int ntfs_sd_add_everyone(ntfs_inode *ni) -{ - /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */ - SECURITY_DESCRIPTOR_RELATIVE *sd; - ACL *acl; - ACCESS_ALLOWED_ACE *ace; - SID *sid; - int ret, sd_len; - - /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */ - /* - * Calculate security descriptor length. We have 2 sub-authorities in - * owner and group SIDs, but structure SID contain only one, so add - * 4 bytes to every SID. - */ - sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) + - sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE); - sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len); - if (!sd) - return -1; - - sd->revision = SECURITY_DESCRIPTOR_REVISION; - sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE; - - sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR)); - sid->revision = SID_REVISION; - sid->sub_authority_count = 2; - sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - sid->identifier_authority.value[5] = 5; - sd->owner = cpu_to_le32((u8*)sid - (u8*)sd); - - sid = (SID*)((u8*)sid + sizeof(SID) + 4); - sid->revision = SID_REVISION; - sid->sub_authority_count = 2; - sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID); - sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS); - sid->identifier_authority.value[5] = 5; - sd->group = cpu_to_le32((u8*)sid - (u8*)sd); - - acl = (ACL*)((u8*)sid + sizeof(SID) + 4); - acl->revision = ACL_REVISION; - acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)); - acl->ace_count = const_cpu_to_le16(1); - sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd); - - ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL)); - ace->type = ACCESS_ALLOWED_ACE_TYPE; - ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE; - ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE)); - ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */ - ace->sid.revision = SID_REVISION; - ace->sid.sub_authority_count = 1; - ace->sid.sub_authority[0] = const_cpu_to_le32(0); - ace->sid.identifier_authority.value[5] = 1; - - ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd, - sd_len); - if (ret) - ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR"); - - free(sd); - return ret; -} - -/* - * Check whether user can access a file in a specific way - * - * Returns 1 if access is allowed, including user is root or no - * user mapping defined - * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX - * 0 and sets errno if there is a problem or if access - * is not allowed - * - * This is used for Posix ACL and checking creation of DOS file names - */ - -int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, - ntfs_inode *ni, - int accesstype) /* access type required (S_Ixxx values) */ -{ - int perm; - int res; - int allow; - struct stat stbuf; - - /* - * Always allow for root unless execution is requested. - * (was checked by fuse until kernel 2.6.29) - * Also always allow if no mapping has been defined - */ - if (!scx->mapping[MAPUSERS] - || (!scx->uid - && (!(accesstype & S_IEXEC) - || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) - allow = 1; - else { - perm = ntfs_get_perm(scx, ni, accesstype); - if (perm >= 0) { - res = EACCES; - switch (accesstype) { - case S_IEXEC: - allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; - break; - case S_IWRITE: - allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0; - break; - case S_IWRITE + S_IEXEC: - allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) - && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); - break; - case S_IREAD: - allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0; - break; - case S_IREAD + S_IEXEC: - allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) - && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); - break; - case S_IREAD + S_IWRITE: - allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) - && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0); - break; - case S_IWRITE + S_IEXEC + S_ISVTX: - if (perm & S_ISVTX) { - if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) - && (stbuf.st_uid == scx->uid)) - allow = 1; - else - allow = 2; - } else - allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) - && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); - break; - case S_IREAD + S_IWRITE + S_IEXEC: - allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) - && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) - && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); - break; - default : - res = EINVAL; - allow = 0; - break; - } - if (!allow) - errno = res; - } else - allow = 0; - } - return (allow); -} - -#if 0 /* not needed any more */ - -/* - * Check whether user can access the parent directory - * of a file in a specific way - * - * Returns true if access is allowed, including user is root and - * no user mapping defined - * - * Sets errno if there is a problem or if not allowed - * - * This is used for Posix ACL and checking creation of DOS file names - */ - -BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, - const char *path, int accesstype) -{ - int allow; - char *dirpath; - char *name; - ntfs_inode *ni; - ntfs_inode *dir_ni; - struct stat stbuf; - - allow = 0; - dirpath = strdup(path); - if (dirpath) { - /* the root of file system is seen as a parent of itself */ - /* is that correct ? */ - name = strrchr(dirpath, '/'); - *name = 0; - dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); - if (dir_ni) { - allow = ntfs_allowed_access(scx, - dir_ni, accesstype); - ntfs_inode_close(dir_ni); - /* - * for an not-owned sticky directory, have to - * check whether file itself is owned - */ - if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX)) - && (allow == 2)) { - ni = ntfs_pathname_to_inode(scx->vol, NULL, - path); - allow = FALSE; - if (ni) { - allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) - && (stbuf.st_uid == scx->uid); - ntfs_inode_close(ni); - } - } - } - free(dirpath); - } - return (allow); /* errno is set if not allowed */ -} - -#endif - -/* - * Define a new owner/group to a file - * - * returns zero if successful - */ - -int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - const SID *usid; - const SID *gsid; - uid_t fileuid; - uid_t filegid; - mode_t mode; - int perm; - BOOL isdir; - int res; -#if POSIXACLS - struct POSIX_SECURITY *pxdesc; - BOOL pxdescbuilt = FALSE; -#endif - - res = 0; - /* get the current owner and mode from cache or security attributes */ - oldattr = (char*)NULL; - cached = fetch_cache(scx,ni); - if (cached) { - fileuid = cached->uid; - filegid = cached->gid; - mode = cached->mode; -#if POSIXACLS - pxdesc = cached->pxdesc; - if (!pxdesc) - res = -1; -#endif - } else { - fileuid = 0; - filegid = 0; - mode = 0; - oldattr = getsecurityattr(scx->vol, ni); - if (oldattr) { - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - oldattr; - gsid = (const SID*) - &oldattr[le32_to_cpu(phead->group)]; -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - usid = (const SID*) - &oldattr[le32_to_cpu(phead->owner)]; -#endif -#if POSIXACLS - pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, - usid, gsid, isdir); - if (pxdesc) { - pxdescbuilt = TRUE; - fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - mode = perm = pxdesc->mode; - } else - res = -1; -#else - mode = perm = ntfs_build_permissions(oldattr, - usid, gsid, isdir); - if (perm >= 0) { - fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - } else - res = -1; -#endif - free(oldattr); - } else - res = -1; - } - if (!res) { - /* check requested by root */ - /* or chgrp requested by owner to an owned group */ - if (!scx->uid - || ((((int)uid < 0) || (uid == fileuid)) - && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) - && (fileuid == scx->uid))) { - /* replace by the new usid and gsid */ - /* or reuse old gid and sid for cacheing */ - if ((int)uid < 0) - uid = fileuid; - if ((int)gid < 0) - gid = filegid; - /* clear setuid and setgid if owner has changed */ - /* unless request originated by root */ - if (uid && (fileuid != uid)) - mode &= 01777; -#if POSIXACLS - res = ntfs_set_owner_mode(scx, ni, uid, gid, - mode, pxdesc); -#else - res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); -#endif - } else { - res = -1; /* neither owner nor root */ - errno = EPERM; - } -#if POSIXACLS - if (pxdescbuilt) - free(pxdesc); -#endif - } else { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error("File has no security descriptor\n"); - res = -1; - errno = EIO; - } - return (res ? -1 : 0); -} - -/* - * Define new owner/group and mode to a file - * - * returns zero if successful - */ - -int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, - uid_t uid, gid_t gid, const mode_t mode) -{ - const struct CACHED_PERMISSIONS *cached; - char *oldattr; - uid_t fileuid; - uid_t filegid; - int res; -#if POSIXACLS - const SECURITY_DESCRIPTOR_RELATIVE *phead; - const SID *usid; - const SID *gsid; - BOOL isdir; - const struct POSIX_SECURITY *oldpxdesc; - struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; - int pxsize; -#endif - - res = 0; - /* get the current owner and mode from cache or security attributes */ - oldattr = (char*)NULL; - cached = fetch_cache(scx,ni); - if (cached) { - fileuid = cached->uid; - filegid = cached->gid; -#if POSIXACLS - oldpxdesc = cached->pxdesc; - if (oldpxdesc) { - /* must copy before merging */ - pxsize = sizeof(struct POSIX_SECURITY) - + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); - newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); - if (newpxdesc) { - memcpy(newpxdesc, oldpxdesc, pxsize); - if (ntfs_merge_mode_posix(newpxdesc, mode)) - res = -1; - } else - res = -1; - } -#endif - } else { - fileuid = 0; - filegid = 0; - oldattr = getsecurityattr(scx->vol, ni); - if (oldattr) { -#if POSIXACLS - isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - != const_cpu_to_le16(0); - phead = (const SECURITY_DESCRIPTOR_RELATIVE*) - oldattr; - gsid = (const SID*) - &oldattr[le32_to_cpu(phead->group)]; -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - usid = (const SID*) - &oldattr[le32_to_cpu(phead->owner)]; -#endif - newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, - usid, gsid, isdir); - if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) - res = -1; - else { - fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); - } -#endif - free(oldattr); - } else - res = -1; - } - if (!res) { - /* check requested by root */ - /* or chgrp requested by owner to an owned group */ - if (!scx->uid - || ((((int)uid < 0) || (uid == fileuid)) - && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) - && (fileuid == scx->uid))) { - /* replace by the new usid and gsid */ - /* or reuse old gid and sid for cacheing */ - if ((int)uid < 0) - uid = fileuid; - if ((int)gid < 0) - gid = filegid; -#if POSIXACLS - res = ntfs_set_owner_mode(scx, ni, uid, gid, - mode, newpxdesc); -#else - res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); -#endif - } else { - res = -1; /* neither owner nor root */ - errno = EPERM; - } - } else { - /* - * Should not happen : a default descriptor is generated - * by getsecurityattr() when there are none - */ - ntfs_log_error("File has no security descriptor\n"); - res = -1; - errno = EIO; - } -#if POSIXACLS - free(newpxdesc); -#endif - return (res ? -1 : 0); -} - -/* - * Build a security id for a descriptor inherited from - * parent directory the Windows way - */ - -static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, - const char *parentattr, BOOL fordir) -{ - const SECURITY_DESCRIPTOR_RELATIVE *pphead; - const ACL *ppacl; - const SID *usid; - const SID *gsid; - BIGSID defusid; - BIGSID defgsid; - int offpacl; - int offowner; - int offgroup; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - ACL *pnacl; - int parentattrsz; - char *newattr; - int newattrsz; - int aclsz; - int usidsz; - int gsidsz; - int pos; - le32 securid; - - parentattrsz = ntfs_attr_size(parentattr); - pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr; - if (scx->mapping[MAPUSERS]) { - usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid); - gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid); - if (!usid) - usid = adminsid; - if (!gsid) - gsid = adminsid; - } else { - /* - * If there is no user mapping, we have to copy owner - * and group from parent directory. - * Windows never has to do that, because it can always - * rely on a user mapping - */ - offowner = le32_to_cpu(pphead->owner); - usid = (const SID*)&parentattr[offowner]; - offgroup = le32_to_cpu(pphead->group); - gsid = (const SID*)&parentattr[offgroup]; - } - /* - * new attribute is smaller than parent's - * except for differences in SIDs which appear in - * owner, group and possible grants and denials in - * generic creator-owner and creator-group ACEs. - * For directories, an ACE may be duplicated for - * access and inheritance, so we double the count. - */ - usidsz = ntfs_sid_size(usid); - gsidsz = ntfs_sid_size(gsid); - newattrsz = parentattrsz + 3*usidsz + 3*gsidsz; - if (fordir) - newattrsz *= 2; - newattr = (char*)ntfs_malloc(newattrsz); - if (newattr) { - pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr; - pnhead->revision = SECURITY_DESCRIPTOR_REVISION; - pnhead->alignment = 0; - pnhead->control = SE_SELF_RELATIVE; - pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - /* - * locate and inherit DACL - * do not test SE_DACL_PRESENT (wrong for "DR Watson") - */ - pnhead->dacl = const_cpu_to_le32(0); - if (pphead->dacl) { - offpacl = le32_to_cpu(pphead->dacl); - ppacl = (const ACL*)&parentattr[offpacl]; - pnacl = (ACL*)&newattr[pos]; - aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); - if (aclsz) { - pnhead->dacl = cpu_to_le32(pos); - pos += aclsz; - pnhead->control |= SE_DACL_PRESENT; - } - } - /* - * locate and inherit SACL - */ - pnhead->sacl = const_cpu_to_le32(0); - if (pphead->sacl) { - offpacl = le32_to_cpu(pphead->sacl); - ppacl = (const ACL*)&parentattr[offpacl]; - pnacl = (ACL*)&newattr[pos]; - aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir); - if (aclsz) { - pnhead->sacl = cpu_to_le32(pos); - pos += aclsz; - pnhead->control |= SE_SACL_PRESENT; - } - } - /* - * inherit or redefine owner - */ - memcpy(&newattr[pos],usid,usidsz); - pnhead->owner = cpu_to_le32(pos); - pos += usidsz; - /* - * inherit or redefine group - */ - memcpy(&newattr[pos],gsid,gsidsz); - pnhead->group = cpu_to_le32(pos); - pos += usidsz; - securid = setsecurityattr(scx->vol, - (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos); - free(newattr); - } else - securid = const_cpu_to_le32(0); - return (securid); -} - -/* - * Get an inherited security id - * - * For Windows compatibility, the normal initial permission setting - * may be inherited from the parent directory instead of being - * defined by the creation arguments. - * - * The following creates an inherited id for that purpose. - * - * Note : the owner and group of parent directory are also - * inherited (which is not the case on Windows) if no user mapping - * is defined. - * - * Returns the inherited id, or zero if not possible (eg on NTFS 1.x) - */ - -le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, - ntfs_inode *dir_ni, BOOL fordir) -{ - struct CACHED_PERMISSIONS *cached; - char *parentattr; - le32 securid; - - securid = const_cpu_to_le32(0); - cached = (struct CACHED_PERMISSIONS*)NULL; - /* - * Try to get inherited id from cache - */ - if (test_nino_flag(dir_ni, v3_Extensions) - && dir_ni->security_id) { - cached = fetch_cache(scx, dir_ni); - if (cached) - securid = (fordir ? cached->inh_dirid - : cached->inh_fileid); - } - /* - * Not cached or not available in cache, compute it all - * Note : if parent directory has no id, it is not cacheable - */ - if (!securid) { - parentattr = getsecurityattr(scx->vol, dir_ni); - if (parentattr) { - securid = build_inherited_id(scx, - parentattr, fordir); - free(parentattr); - /* - * Store the result into cache for further use - */ - if (securid) { - cached = fetch_cache(scx, dir_ni); - if (cached) { - if (fordir) - cached->inh_dirid = securid; - else - cached->inh_fileid = securid; - } - } - } - } - return (securid); -} - -/* - * Link a group to a member of group - * - * Returns 0 if OK, -1 (and errno set) if error - */ - -static int link_single_group(struct MAPPING *usermapping, struct passwd *user, - gid_t gid) -{ - struct group *group; - char **grmem; - int grcnt; - gid_t *groups; - int res; - - res = 0; - group = getgrgid(gid); - if (group && group->gr_mem) { - grcnt = usermapping->grcnt; - groups = usermapping->groups; - grmem = group->gr_mem; - while (*grmem && strcmp(user->pw_name, *grmem)) - grmem++; - if (*grmem) { - if (!grcnt) - groups = (gid_t*)malloc(sizeof(gid_t)); - else - groups = (gid_t*)realloc(groups, - (grcnt+1)*sizeof(gid_t)); - if (groups) - groups[grcnt++] = gid; - else { - res = -1; - errno = ENOMEM; - } - } - usermapping->grcnt = grcnt; - usermapping->groups = groups; - } - return (res); -} - - -/* - * Statically link group to users - * This is based on groups defined in /etc/group and does not take - * the groups dynamically set by setgroups() nor any changes in - * /etc/group into account - * - * Only mapped groups and root group are linked to mapped users - * - * Returns 0 if OK, -1 (and errno set) if error - * - */ - -static int link_group_members(struct SECURITY_CONTEXT *scx) -{ - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - struct passwd *user; - int res; - - res = 0; - for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res; - usermapping=usermapping->next) { - usermapping->grcnt = 0; - usermapping->groups = (gid_t*)NULL; - user = getpwuid(usermapping->xid); - if (user && user->pw_name) { - for (groupmapping=scx->mapping[MAPGROUPS]; - groupmapping && !res; - groupmapping=groupmapping->next) { - if (link_single_group(usermapping, user, - groupmapping->xid)) - res = -1; - } - if (!res && link_single_group(usermapping, - user, (gid_t)0)) - res = -1; - } - } - return (res); -} - -/* - * Apply default single user mapping - * returns zero if successful - */ - -static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const SID *usid) -{ - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - SID *sid; - int sidsz; - int res; - - res = -1; - sidsz = ntfs_sid_size(usid); - sid = (SID*)ntfs_malloc(sidsz); - if (sid) { - memcpy(sid,usid,sidsz); - usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); - if (usermapping) { - groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); - if (groupmapping) { - usermapping->sid = sid; - usermapping->xid = uid; - usermapping->next = (struct MAPPING*)NULL; - groupmapping->sid = sid; - groupmapping->xid = gid; - groupmapping->next = (struct MAPPING*)NULL; - scx->mapping[MAPUSERS] = usermapping; - scx->mapping[MAPGROUPS] = groupmapping; - res = 0; - } - } - } - return (res); -} - -/* - * Make sure there are no ambiguous mapping - * Ambiguous mapping may lead to undesired configurations and - * we had rather be safe until the consequences are understood - */ - -#if 0 /* not activated for now */ - -static BOOL check_mapping(const struct MAPPING *usermapping, - const struct MAPPING *groupmapping) -{ - const struct MAPPING *mapping1; - const struct MAPPING *mapping2; - BOOL ambiguous; - - ambiguous = FALSE; - for (mapping1=usermapping; mapping1; mapping1=mapping1->next) - for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) - if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { - if (mapping1->xid != mapping2->xid) - ambiguous = TRUE; - } else { - if (mapping1->xid == mapping2->xid) - ambiguous = TRUE; - } - for (mapping1=groupmapping; mapping1; mapping1=mapping1->next) - for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next) - if (ntfs_same_sid(mapping1->sid,mapping2->sid)) { - if (mapping1->xid != mapping2->xid) - ambiguous = TRUE; - } else { - if (mapping1->xid == mapping2->xid) - ambiguous = TRUE; - } - return (ambiguous); -} - -#endif - -#if 0 /* not used any more */ - -/* - * Try and apply default single user mapping - * returns zero if successful - */ - -static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - ntfs_inode *ni; - char *securattr; - const SID *usid; - int res; - - res = -1; - ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); - if (ni) { - securattr = getsecurityattr(scx->vol, ni); - if (securattr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; - usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; - if (ntfs_is_user_sid(usid)) - res = ntfs_do_default_mapping(scx, - scx->uid, scx->gid, usid); - free(securattr); - } - ntfs_inode_close(ni); - } - return (res); -} - -#endif - -/* - * Basic read from a user mapping file on another volume - */ - -static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused))) -{ - return (read(*(int*)fileid, buf, size)); -} - - -/* - * Read from a user mapping file on current NTFS partition - */ - -static int localread(void *fileid, char *buf, size_t size, off_t offs) -{ - return (ntfs_attr_data_read((ntfs_inode*)fileid, - AT_UNNAMED, 0, buf, size, offs)); -} - -/* - * Build the user mapping - * - according to a mapping file if defined (or default present), - * - or try default single user mapping if possible - * - * The mapping is specific to a mounted device - * No locking done, mounting assumed non multithreaded - * - * returns zero if mapping is successful - * (failure should not be interpreted as an error) - */ - -int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, - BOOL allowdef) -{ - struct MAPLIST *item; - struct MAPLIST *firstitem; - struct MAPPING *usermapping; - struct MAPPING *groupmapping; - ntfs_inode *ni; - int fd; - static struct { - u8 revision; - u8 levels; - be16 highbase; - be32 lowbase; - le32 level1; - le32 level2; - le32 level3; - le32 level4; - le32 level5; - } defmap = { - 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), - const_cpu_to_le32(21), - const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), - const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) - } ; - - /* be sure not to map anything until done */ - scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; - scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL; - - if (!usermap_path) usermap_path = MAPPINGFILE; - if (usermap_path[0] == '/') { - fd = open(usermap_path,O_RDONLY); - if (fd > 0) { - firstitem = ntfs_read_mapping(basicread, (void*)&fd); - close(fd); - } else - firstitem = (struct MAPLIST*)NULL; - } else { - ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path); - if (ni) { - firstitem = ntfs_read_mapping(localread, ni); - ntfs_inode_close(ni); - } else - firstitem = (struct MAPLIST*)NULL; - } - - - if (firstitem) { - usermapping = ntfs_do_user_mapping(firstitem); - groupmapping = ntfs_do_group_mapping(firstitem); - if (usermapping && groupmapping) { - scx->mapping[MAPUSERS] = usermapping; - scx->mapping[MAPGROUPS] = groupmapping; - } else - ntfs_log_error("There were no valid user or no valid group\n"); - /* now we can free the memory copy of input text */ - /* and rely on internal representation */ - while (firstitem) { - item = firstitem->next; - free(firstitem); - firstitem = item; - } - } else { - /* no mapping file, try a default mapping */ - if (allowdef) { - if (!ntfs_do_default_mapping(scx, - 0, 0, (const SID*)&defmap)) - ntfs_log_info("Using default user mapping\n"); - } - } - return (!scx->mapping[MAPUSERS] || link_group_members(scx)); -} - -#ifdef HAVE_SETXATTR /* extended attributes interface required */ - -/* - * Get the ntfs attribute into an extended attribute - * The attribute is returned according to cpu endianness - */ - -int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) -{ - u32 attrib; - size_t outsize; - - outsize = 0; /* default to no data and no error */ - if (ni) { - attrib = le32_to_cpu(ni->flags); - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); - else - attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); - if (!attrib) - attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); - outsize = sizeof(FILE_ATTR_FLAGS); - if (size >= outsize) { - if (value) - memcpy(value,&attrib,outsize); - else - errno = EINVAL; - } - } - return (outsize ? (int)outsize : -errno); -} - -/* - * Get the ntfs attributes from an inode - * The attributes are returned according to cpu endianness - * - * Returns the attributes if successful (cannot be zero) - * 0 if failed (errno to tell why) - */ - -u32 ntfs_get_inode_attributes(ntfs_inode *ni) -{ - u32 attrib = -1; - - if (ni) { - attrib = le32_to_cpu(ni->flags); - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) - attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY); - else - attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY); - if (!attrib) - attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL); - } else - errno = EINVAL; - return (attrib); -} - -/* - * Set the ntfs attributes on an inode - * The attribute is expected according to cpu endianness - * - * Returns 0 if successful - * -1 if failed (errno to tell why) - */ - -int ntfs_set_inode_attributes(ntfs_inode *ni, u32 attrib) -{ - le32 settable; - ATTR_FLAGS dirflags; - int res; - - res = -1; - if (ni) { - settable = FILE_ATTR_SETTABLE; - res = 0; - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { - /* - * Accept changing compression for a directory - * and set index root accordingly - */ - settable |= FILE_ATTR_COMPRESSED; - if ((ni->flags ^ cpu_to_le32(attrib)) - & FILE_ATTR_COMPRESSED) { - if (ni->flags & FILE_ATTR_COMPRESSED) - dirflags = const_cpu_to_le16(0); - else - dirflags = ATTR_IS_COMPRESSED; - res = ntfs_attr_set_flags(ni, AT_INDEX_ROOT, - NTFS_INDEX_I30, 4, dirflags, - ATTR_COMPRESSION_MASK); - } - } - if (!res) { - ni->flags = (ni->flags & ~settable) - | (cpu_to_le32(attrib) & settable); - NInoFileNameSetDirty(ni); - NInoSetDirty(ni); - } - } else - errno = EINVAL; - return (res); -} - -/* - * Return the ntfs attribute into an extended attribute - * The attribute is expected according to cpu endianness - * - * Returns 0, or -1 if there is a problem - */ - -int ntfs_set_ntfs_attrib(ntfs_inode *ni, - const char *value, size_t size, int flags) -{ - u32 attrib; - int res; - - res = -1; - if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) { - if (!(flags & XATTR_CREATE)) { - /* copy to avoid alignment problems */ - memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS)); - res = ntfs_set_inode_attributes(ni, attrib); - } else - errno = EEXIST; - } else - errno = EINVAL; - return (res ? -1 : 0); -} - -#endif /* HAVE_SETXATTR */ - -/* - * Open $Secure once for all - * returns zero if it succeeds - * non-zero if it fails. This is not an error (on NTFS v1.x) - */ - - -int ntfs_open_secure(ntfs_volume *vol) -{ - ntfs_inode *ni; - int res; - - res = -1; - vol->secure_ni = (ntfs_inode*)NULL; - vol->secure_xsii = (ntfs_index_context*)NULL; - vol->secure_xsdh = (ntfs_index_context*)NULL; - if (vol->major_ver >= 3) { - /* make sure this is a genuine $Secure inode 9 */ - ni = ntfs_pathname_to_inode(vol, NULL, "$Secure"); - if (ni && (ni->mft_no == 9)) { - vol->secure_reentry = 0; - vol->secure_xsii = ntfs_index_ctx_get(ni, - sii_stream, 4); - vol->secure_xsdh = ntfs_index_ctx_get(ni, - sdh_stream, 4); - if (ni && vol->secure_xsii && vol->secure_xsdh) { - vol->secure_ni = ni; - res = 0; - } - } - } - return (res); -} - -/* - * Final cleaning - * Allocated memory is freed to facilitate the detection of memory leaks - */ - -void ntfs_close_secure(struct SECURITY_CONTEXT *scx) -{ - ntfs_volume *vol; - - vol = scx->vol; - if (vol->secure_ni) { - ntfs_index_ctx_put(vol->secure_xsii); - ntfs_index_ctx_put(vol->secure_xsdh); - ntfs_inode_close(vol->secure_ni); - - } - ntfs_free_mapping(scx->mapping); - free_caches(scx); -} - -/* - * API for direct access to security descriptors - * based on Win32 API - */ - - -/* - * Selective feeding of a security descriptor into user buffer - * - * Returns TRUE if successful - */ - -static BOOL feedsecurityattr(const char *attr, u32 selection, - char *buf, u32 buflen, u32 *psize) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - SECURITY_DESCRIPTOR_RELATIVE *pnhead; - const ACL *pdacl; - const ACL *psacl; - const SID *pusid; - const SID *pgsid; - unsigned int offdacl; - unsigned int offsacl; - unsigned int offowner; - unsigned int offgroup; - unsigned int daclsz; - unsigned int saclsz; - unsigned int usidsz; - unsigned int gsidsz; - unsigned int size; /* size of requested attributes */ - BOOL ok; - unsigned int pos; - unsigned int avail; - le16 control; - - avail = 0; - control = SE_SELF_RELATIVE; - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; - size = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - - /* locate DACL if requested and available */ - if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) { - offdacl = le32_to_cpu(phead->dacl); - pdacl = (const ACL*)&attr[offdacl]; - daclsz = le16_to_cpu(pdacl->size); - size += daclsz; - avail |= DACL_SECURITY_INFORMATION; - } else - offdacl = daclsz = 0; - - /* locate owner if requested and available */ - offowner = le32_to_cpu(phead->owner); - if (offowner && (selection & OWNER_SECURITY_INFORMATION)) { - /* find end of USID */ - pusid = (const SID*)&attr[offowner]; - usidsz = ntfs_sid_size(pusid); - size += usidsz; - avail |= OWNER_SECURITY_INFORMATION; - } else - offowner = usidsz = 0; - - /* locate group if requested and available */ - offgroup = le32_to_cpu(phead->group); - if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) { - /* find end of GSID */ - pgsid = (const SID*)&attr[offgroup]; - gsidsz = ntfs_sid_size(pgsid); - size += gsidsz; - avail |= GROUP_SECURITY_INFORMATION; - } else - offgroup = gsidsz = 0; - - /* locate SACL if requested and available */ - if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) { - /* find end of SACL */ - offsacl = le32_to_cpu(phead->sacl); - psacl = (const ACL*)&attr[offsacl]; - saclsz = le16_to_cpu(psacl->size); - size += saclsz; - avail |= SACL_SECURITY_INFORMATION; - } else - offsacl = saclsz = 0; - - /* - * Check having enough size in destination buffer - * (required size is returned nevertheless so that - * the request can be reissued with adequate size) - */ - if (size > buflen) { - *psize = size; - errno = EINVAL; - ok = FALSE; - } else { - if (selection & OWNER_SECURITY_INFORMATION) - control |= phead->control & SE_OWNER_DEFAULTED; - if (selection & GROUP_SECURITY_INFORMATION) - control |= phead->control & SE_GROUP_DEFAULTED; - if (selection & DACL_SECURITY_INFORMATION) - control |= phead->control - & (SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PROTECTED); - if (selection & SACL_SECURITY_INFORMATION) - control |= phead->control - & (SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_AUTO_INHERITED - | SE_SACL_PROTECTED); - /* - * copy header and feed new flags, even if no detailed data - */ - memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); - pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf; - pnhead->control = control; - pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - - /* copy DACL if requested and available */ - if (selection & avail & DACL_SECURITY_INFORMATION) { - pnhead->dacl = cpu_to_le32(pos); - memcpy(&buf[pos],&attr[offdacl],daclsz); - pos += daclsz; - } else - pnhead->dacl = const_cpu_to_le32(0); - - /* copy SACL if requested and available */ - if (selection & avail & SACL_SECURITY_INFORMATION) { - pnhead->sacl = cpu_to_le32(pos); - memcpy(&buf[pos],&attr[offsacl],saclsz); - pos += saclsz; - } else - pnhead->sacl = const_cpu_to_le32(0); - - /* copy owner if requested and available */ - if (selection & avail & OWNER_SECURITY_INFORMATION) { - pnhead->owner = cpu_to_le32(pos); - memcpy(&buf[pos],&attr[offowner],usidsz); - pos += usidsz; - } else - pnhead->owner = const_cpu_to_le32(0); - - /* copy group if requested and available */ - if (selection & avail & GROUP_SECURITY_INFORMATION) { - pnhead->group = cpu_to_le32(pos); - memcpy(&buf[pos],&attr[offgroup],gsidsz); - pos += gsidsz; - } else - pnhead->group = const_cpu_to_le32(0); - if (pos != size) - ntfs_log_error("Error in security descriptor size\n"); - *psize = size; - ok = TRUE; - } - - return (ok); -} - -/* - * Merge a new security descriptor into the old one - * and assign to designated file - * - * Returns TRUE if successful - */ - -static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr, - const char *newattr, u32 selection, ntfs_inode *ni) -{ - const SECURITY_DESCRIPTOR_RELATIVE *oldhead; - const SECURITY_DESCRIPTOR_RELATIVE *newhead; - SECURITY_DESCRIPTOR_RELATIVE *targhead; - const ACL *pdacl; - const ACL *psacl; - const SID *powner; - const SID *pgroup; - int offdacl; - int offsacl; - int offowner; - int offgroup; - unsigned int size; - le16 control; - char *target; - int pos; - int oldattrsz; - int newattrsz; - BOOL ok; - - ok = FALSE; /* default return */ - oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; - newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr; - oldattrsz = ntfs_attr_size(oldattr); - newattrsz = ntfs_attr_size(newattr); - target = (char*)ntfs_malloc(oldattrsz + newattrsz); - if (target) { - targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target; - pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE); - control = SE_SELF_RELATIVE; - /* - * copy new DACL if selected - * or keep old DACL if any - */ - if ((selection & DACL_SECURITY_INFORMATION) ? - newhead->dacl : oldhead->dacl) { - if (selection & DACL_SECURITY_INFORMATION) { - offdacl = le32_to_cpu(newhead->dacl); - pdacl = (const ACL*)&newattr[offdacl]; - } else { - offdacl = le32_to_cpu(oldhead->dacl); - pdacl = (const ACL*)&oldattr[offdacl]; - } - size = le16_to_cpu(pdacl->size); - memcpy(&target[pos], pdacl, size); - targhead->dacl = cpu_to_le32(pos); - pos += size; - } else - targhead->dacl = const_cpu_to_le32(0); - if (selection & DACL_SECURITY_INFORMATION) { - control |= newhead->control - & (SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_PROTECTED); - if (newhead->control & SE_DACL_AUTO_INHERIT_REQ) - control |= SE_DACL_AUTO_INHERITED; - } else - control |= oldhead->control - & (SE_DACL_PRESENT - | SE_DACL_DEFAULTED - | SE_DACL_AUTO_INHERITED - | SE_DACL_PROTECTED); - /* - * copy new SACL if selected - * or keep old SACL if any - */ - if ((selection & SACL_SECURITY_INFORMATION) ? - newhead->sacl : oldhead->sacl) { - if (selection & SACL_SECURITY_INFORMATION) { - offsacl = le32_to_cpu(newhead->sacl); - psacl = (const ACL*)&newattr[offsacl]; - } else { - offsacl = le32_to_cpu(oldhead->sacl); - psacl = (const ACL*)&oldattr[offsacl]; - } - size = le16_to_cpu(psacl->size); - memcpy(&target[pos], psacl, size); - targhead->sacl = cpu_to_le32(pos); - pos += size; - } else - targhead->sacl = const_cpu_to_le32(0); - if (selection & SACL_SECURITY_INFORMATION) { - control |= newhead->control - & (SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_PROTECTED); - if (newhead->control & SE_SACL_AUTO_INHERIT_REQ) - control |= SE_SACL_AUTO_INHERITED; - } else - control |= oldhead->control - & (SE_SACL_PRESENT - | SE_SACL_DEFAULTED - | SE_SACL_AUTO_INHERITED - | SE_SACL_PROTECTED); - /* - * copy new OWNER if selected - * or keep old OWNER if any - */ - if ((selection & OWNER_SECURITY_INFORMATION) ? - newhead->owner : oldhead->owner) { - if (selection & OWNER_SECURITY_INFORMATION) { - offowner = le32_to_cpu(newhead->owner); - powner = (const SID*)&newattr[offowner]; - } else { - offowner = le32_to_cpu(oldhead->owner); - powner = (const SID*)&oldattr[offowner]; - } - size = ntfs_sid_size(powner); - memcpy(&target[pos], powner, size); - targhead->owner = cpu_to_le32(pos); - pos += size; - } else - targhead->owner = const_cpu_to_le32(0); - if (selection & OWNER_SECURITY_INFORMATION) - control |= newhead->control & SE_OWNER_DEFAULTED; - else - control |= oldhead->control & SE_OWNER_DEFAULTED; - /* - * copy new GROUP if selected - * or keep old GROUP if any - */ - if ((selection & GROUP_SECURITY_INFORMATION) ? - newhead->group : oldhead->group) { - if (selection & GROUP_SECURITY_INFORMATION) { - offgroup = le32_to_cpu(newhead->group); - pgroup = (const SID*)&newattr[offgroup]; - control |= newhead->control - & SE_GROUP_DEFAULTED; - } else { - offgroup = le32_to_cpu(oldhead->group); - pgroup = (const SID*)&oldattr[offgroup]; - control |= oldhead->control - & SE_GROUP_DEFAULTED; - } - size = ntfs_sid_size(pgroup); - memcpy(&target[pos], pgroup, size); - targhead->group = cpu_to_le32(pos); - pos += size; - } else - targhead->group = const_cpu_to_le32(0); - if (selection & GROUP_SECURITY_INFORMATION) - control |= newhead->control & SE_GROUP_DEFAULTED; - else - control |= oldhead->control & SE_GROUP_DEFAULTED; - targhead->revision = SECURITY_DESCRIPTOR_REVISION; - targhead->alignment = 0; - targhead->control = control; - ok = !update_secur_descr(vol, target, ni); - free(target); - } - return (ok); -} - -int ntfs_get_inode_security(ntfs_inode *ni, u32 selection, - char *buf, u32 buflen, u32 *psize) -{ - int res; - char *attr; - - res = 0; - if (ni) { - attr = getsecurityattr(ni->vol, ni); - if (attr) { - if (feedsecurityattr(attr, selection, - buf, buflen, psize)) { - if (test_nino_flag(ni, v3_Extensions) - && ni->security_id) - res = le32_to_cpu( - ni->security_id); - else - res = -1; - } - free(attr); - } - } else - errno = EINVAL; - return (res); -} - -int ntfs_set_inode_security(ntfs_inode *ni, u32 selection, const char *attr) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - int attrsz; - BOOL missing; - char *oldattr; - int res; - - res = -1; /* default return */ - if (ni) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; - attrsz = ntfs_attr_size(attr); - /* if selected, owner and group must be present or defaulted */ - missing = ((selection & OWNER_SECURITY_INFORMATION) - && !phead->owner - && !(phead->control & SE_OWNER_DEFAULTED)) - || ((selection & GROUP_SECURITY_INFORMATION) - && !phead->group - && !(phead->control & SE_GROUP_DEFAULTED)); - if (!missing - && (phead->control & SE_SELF_RELATIVE) - && ntfs_valid_descr(attr, attrsz)) { - oldattr = getsecurityattr(ni->vol, ni); - if (oldattr) { - if (mergesecurityattr(ni->vol, oldattr, attr, - selection, ni)) { - res = 0; - } - free(oldattr); - } - } else - errno = EINVAL; - } else - errno = EINVAL; - return (res); -} - -/* - * Return the security descriptor of a file - * This is intended to be similar to GetFileSecurity() from Win32 - * in order to facilitate the development of portable tools - * - * returns zero if unsuccessful (following Win32 conventions) - * -1 if no securid - * the securid if any - * - * The Win32 API is : - * - * BOOL WINAPI GetFileSecurity( - * __in LPCTSTR lpFileName, - * __in SECURITY_INFORMATION RequestedInformation, - * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, - * __in DWORD nLength, - * __out LPDWORD lpnLengthNeeded - * ); - * - */ - -int ntfs_get_file_security(struct SECURITY_API *scapi, - const char *path, u32 selection, - char *buf, u32 buflen, u32 *psize) -{ - ntfs_inode *ni; - char *attr; - int res; - - res = 0; /* default return */ - if (scapi && (scapi->magic == MAGIC_API)) { - ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); - if (ni) { - attr = getsecurityattr(scapi->security.vol, ni); - if (attr) { - if (feedsecurityattr(attr,selection, - buf,buflen,psize)) { - if (test_nino_flag(ni, v3_Extensions) - && ni->security_id) - res = le32_to_cpu( - ni->security_id); - else - res = -1; - } - free(attr); - } - ntfs_inode_close(ni); - } else - errno = ENOENT; - if (!res) *psize = 0; - } else - errno = EINVAL; /* do not clear *psize */ - return (res); -} - - -/* - * Set the security descriptor of a file or directory - * This is intended to be similar to SetFileSecurity() from Win32 - * in order to facilitate the development of portable tools - * - * returns zero if unsuccessful (following Win32 conventions) - * -1 if no securid - * the securid if any - * - * The Win32 API is : - * - * BOOL WINAPI SetFileSecurity( - * __in LPCTSTR lpFileName, - * __in SECURITY_INFORMATION SecurityInformation, - * __in PSECURITY_DESCRIPTOR pSecurityDescriptor - * ); - */ - -int ntfs_set_file_security(struct SECURITY_API *scapi, - const char *path, u32 selection, const char *attr) -{ - const SECURITY_DESCRIPTOR_RELATIVE *phead; - ntfs_inode *ni; - int attrsz; - BOOL missing; - char *oldattr; - int res; - - res = 0; /* default return */ - if (scapi && (scapi->magic == MAGIC_API) && attr) { - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr; - attrsz = ntfs_attr_size(attr); - /* if selected, owner and group must be present or defaulted */ - missing = ((selection & OWNER_SECURITY_INFORMATION) - && !phead->owner - && !(phead->control & SE_OWNER_DEFAULTED)) - || ((selection & GROUP_SECURITY_INFORMATION) - && !phead->group - && !(phead->control & SE_GROUP_DEFAULTED)); - if (!missing - && (phead->control & SE_SELF_RELATIVE) - && ntfs_valid_descr(attr, attrsz)) { - ni = ntfs_pathname_to_inode(scapi->security.vol, - NULL, path); - if (ni) { - oldattr = getsecurityattr(scapi->security.vol, - ni); - if (oldattr) { - if (mergesecurityattr( - scapi->security.vol, - oldattr, attr, - selection, ni)) { - if (test_nino_flag(ni, - v3_Extensions)) - res = le32_to_cpu( - ni->security_id); - else - res = -1; - } - free(oldattr); - } - ntfs_inode_close(ni); - } - } else - errno = EINVAL; - } else - errno = EINVAL; - return (res); -} - - -/* - * Return the attributes of a file - * This is intended to be similar to GetFileAttributes() from Win32 - * in order to facilitate the development of portable tools - * - * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES) - * - * The Win32 API is : - * - * DWORD WINAPI GetFileAttributes( - * __in LPCTSTR lpFileName - * ); - */ - -int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path) -{ - ntfs_inode *ni; - s32 attrib; - - attrib = -1; /* default return */ - if (scapi && (scapi->magic == MAGIC_API) && path) { - ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); - if (ni) { - attrib = ntfs_get_inode_attributes(ni); - ntfs_inode_close(ni); - } else - errno = ENOENT; - } else - errno = EINVAL; /* do not clear *psize */ - return (attrib); -} - - -/* - * Set attributes to a file or directory - * This is intended to be similar to SetFileAttributes() from Win32 - * in order to facilitate the development of portable tools - * - * Only a few flags can be set (same list as Win32) - * - * returns zero if unsuccessful (following Win32 conventions) - * nonzero if successful - * - * The Win32 API is : - * - * BOOL WINAPI SetFileAttributes( - * __in LPCTSTR lpFileName, - * __in DWORD dwFileAttributes - * ); - */ - -BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi, - const char *path, s32 attrib) -{ - ntfs_inode *ni; - int res; - - res = 0; /* default return */ - if (scapi && (scapi->magic == MAGIC_API) && path) { - ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); - if (ni) { - /* Win32 convention here : -1 means successful */ - if (!ntfs_set_inode_attributes(ni, attrib)) - res = -1; - if (ntfs_inode_close(ni)) - res = 0; - } else - errno = ENOENT; - } - return (res); -} - - -BOOL ntfs_read_directory(struct SECURITY_API *scapi, - const char *path, ntfs_filldir_t callback, void *context) -{ - ntfs_inode *ni; - BOOL ok; - s64 pos; - - ok = FALSE; /* default return */ - if (scapi && (scapi->magic == MAGIC_API) && callback) { - ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); - if (ni) { - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { - pos = 0; - ntfs_readdir(ni,&pos,context,callback); - ok = !ntfs_inode_close(ni); - } else { - ntfs_inode_close(ni); - errno = ENOTDIR; - } - } else - errno = ENOENT; - } else - errno = EINVAL; /* do not clear *psize */ - return (ok); -} - -/* - * read $SDS (for auditing security data) - * - * Returns the number or read bytes, or -1 if there is an error - */ - -int ntfs_read_sds(struct SECURITY_API *scapi, - char *buf, u32 size, u32 offset) -{ - int got; - - got = -1; /* default return */ - if (scapi && (scapi->magic == MAGIC_API)) { - if (scapi->security.vol->secure_ni) - got = ntfs_attr_data_read(scapi->security.vol->secure_ni, - STREAM_SDS, 4, buf, size, offset); - else - errno = EOPNOTSUPP; - } else - errno = EINVAL; - return (got); -} - -/* - * read $SII (for auditing security data) - * - * Returns next entry, or NULL if there is an error - */ - -INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi, - INDEX_ENTRY *entry) -{ - SII_INDEX_KEY key; - INDEX_ENTRY *ret; - BOOL found; - ntfs_index_context *xsii; - - ret = (INDEX_ENTRY*)NULL; /* default return */ - if (scapi && (scapi->magic == MAGIC_API)) { - xsii = scapi->security.vol->secure_xsii; - if (xsii) { - if (!entry) { - key.security_id = const_cpu_to_le32(0); - found = !ntfs_index_lookup((char*)&key, - sizeof(SII_INDEX_KEY), xsii); - /* not supposed to find */ - if (!found && (errno == ENOENT)) - ret = xsii->entry; - } else - ret = ntfs_index_next(entry,xsii); - if (!ret) - errno = ENODATA; - } else - errno = EOPNOTSUPP; - } else - errno = EINVAL; - return (ret); -} - -/* - * read $SDH (for auditing security data) - * - * Returns next entry, or NULL if there is an error - */ - -INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi, - INDEX_ENTRY *entry) -{ - SDH_INDEX_KEY key; - INDEX_ENTRY *ret; - BOOL found; - ntfs_index_context *xsdh; - - ret = (INDEX_ENTRY*)NULL; /* default return */ - if (scapi && (scapi->magic == MAGIC_API)) { - xsdh = scapi->security.vol->secure_xsdh; - if (xsdh) { - if (!entry) { - key.hash = const_cpu_to_le32(0); - key.security_id = const_cpu_to_le32(0); - found = !ntfs_index_lookup((char*)&key, - sizeof(SDH_INDEX_KEY), xsdh); - /* not supposed to find */ - if (!found && (errno == ENOENT)) - ret = xsdh->entry; - } else - ret = ntfs_index_next(entry,xsdh); - if (!ret) - errno = ENODATA; - } else errno = ENOTSUP; - } else - errno = EINVAL; - return (ret); -} - -/* - * Get the mapped user SID - * A buffer of 40 bytes has to be supplied - * - * returns the size of the SID, or zero and errno set if not found - */ - -int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf) -{ - const SID *usid; - BIGSID defusid; - int size; - - size = 0; - if (scapi && (scapi->magic == MAGIC_API)) { - usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid); - if (usid) { - size = ntfs_sid_size(usid); - memcpy(buf,usid,size); - } else - errno = ENODATA; - } else - errno = EINVAL; - return (size); -} - -/* - * Get the mapped group SID - * A buffer of 40 bytes has to be supplied - * - * returns the size of the SID, or zero and errno set if not found - */ - -int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf) -{ - const SID *gsid; - BIGSID defgsid; - int size; - - size = 0; - if (scapi && (scapi->magic == MAGIC_API)) { - gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid); - if (gsid) { - size = ntfs_sid_size(gsid); - memcpy(buf,gsid,size); - } else - errno = ENODATA; - } else - errno = EINVAL; - return (size); -} - -/* - * Get the user mapped to a SID - * - * returns the uid, or -1 if not found - */ - -int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid) -{ - int uid; - - uid = -1; - if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) { - if (ntfs_same_sid(usid,adminsid)) - uid = 0; - else { - uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid); - if (!uid) { - uid = -1; - errno = ENODATA; - } - } - } else - errno = EINVAL; - return (uid); -} - -/* - * Get the group mapped to a SID - * - * returns the uid, or -1 if not found - */ - -int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid) -{ - int gid; - - gid = -1; - if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) { - if (ntfs_same_sid(gsid,adminsid)) - gid = 0; - else { - gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid); - if (!gid) { - gid = -1; - errno = ENODATA; - } - } - } else - errno = EINVAL; - return (gid); -} - -/* - * Initializations before calling ntfs_get_file_security() - * ntfs_set_file_security() and ntfs_read_directory() - * - * Only allowed for root - * - * Returns an (obscured) struct SECURITY_API* needed for further calls - * NULL if not root (EPERM) or device is mounted (EBUSY) - */ - -struct SECURITY_API *ntfs_initialize_file_security(const char *device, - unsigned long flags) -{ - ntfs_volume *vol; - unsigned long mntflag; - int mnt; - struct SECURITY_API *scapi; - struct SECURITY_CONTEXT *scx; - - scapi = (struct SECURITY_API*)NULL; - mnt = ntfs_check_if_mounted(device, &mntflag); - if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) { - vol = ntfs_mount(device, flags); - if (vol) { - scapi = (struct SECURITY_API*) - ntfs_malloc(sizeof(struct SECURITY_API)); - if (!ntfs_volume_get_free_space(vol) - && scapi) { - scapi->magic = MAGIC_API; - scapi->seccache = (struct PERMISSIONS_CACHE*)NULL; - scx = &scapi->security; - scx->vol = vol; - scx->uid = getuid(); - scx->gid = getgid(); - scx->pseccache = &scapi->seccache; - scx->vol->secure_flags = 0; - /* accept no mapping and no $Secure */ - ntfs_build_mapping(scx,(const char*)NULL,TRUE); - ntfs_open_secure(vol); - } else { - if (scapi) - free(scapi); - else - errno = ENOMEM; - mnt = ntfs_umount(vol,FALSE); - scapi = (struct SECURITY_API*)NULL; - } - } - } else - if (getuid()) - errno = EPERM; - else - errno = EBUSY; - return (scapi); -} - -/* - * Leaving after ntfs_initialize_file_security() - * - * Returns FALSE if FAILED - */ - -BOOL ntfs_leave_file_security(struct SECURITY_API *scapi) -{ - int ok; - ntfs_volume *vol; - - ok = FALSE; - if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) { - vol = scapi->security.vol; - ntfs_close_secure(&scapi->security); - free(scapi); - if (!ntfs_umount(vol, 0)) - ok = TRUE; - } - return (ok); -} - -- 2.43.0 From d21b498087a5e74013e257e8672ba616707f25c3 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 08:58:42 -0500 Subject: [PATCH 09/16] Document libntfs-3g versions allowed --- README | 5 ++++- configure.ac | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README b/README index 364cb77f..086fa5ee 100644 --- a/README +++ b/README @@ -94,7 +94,10 @@ server for PXE booting. See the main page `doc/mkwinpeiso.1' for more details. * libntfs-3g Unless configured with --without-ntfs-3g, wimlib requires the library - and headers for libntfs-3g to be installed. + and headers for libntfs-3g version 2011-4-12 or later to be installed. + Versions dated 2010-3-6 and earlier do not work because they are missing + the header xattrs.h (and the file xattrs.c, which contains functions we + need). * cdrkit (optional) * mtools (optional) diff --git a/configure.ac b/configure.ac index c2156614..179c2436 100644 --- a/configure.ac +++ b/configure.ac @@ -145,7 +145,7 @@ if test "x$WITH_NTFS_3G" = "xyes"; then AC_DEFINE([WITH_NTFS_3G], [1], [Define to 1 to enable support for NTFS-specific information]) - AC_CHECK_LIB([ntfs-3g], [ntfs_set_file_security], [], + AC_CHECK_LIB([ntfs-3g], [ntfs_mount], [], [AC_MSG_ERROR([Cannot find libntfs-3g. Without libntfs-3g, wimlib cannot include support for capturing and applying WIMs on NTFS filesystems while preserving NTFS-specific data -- 2.43.0 From fec65d853bbcfdb4639d8446db8d3c681c7ae00d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 12:58:09 -0500 Subject: [PATCH 10/16] security descriptor application fix --- doc/mkwinpeimg.1.in | 2 +- src/ntfs-apply.c | 6 +++--- src/security.c | 29 +++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/doc/mkwinpeimg.1.in b/doc/mkwinpeimg.1.in index dd6084fd..0db9d6e0 100644 --- a/doc/mkwinpeimg.1.in +++ b/doc/mkwinpeimg.1.in @@ -1,4 +1,4 @@ -.TH MKWINPEIMG "1" "May 2012" "mkwinpeimg (wimlib @VERSION@)" "User Commands" +.TH MKWINPEIMG "1" "September 2012" "mkwinpeimg (wimlib @VERSION@)" "User Commands" .SH NAME mkwinpeimg \- Make a customized bootable image of Windows PE .SH SYNOPSIS diff --git a/src/ntfs-apply.c b/src/ntfs-apply.c index 23526810..d9f6d37a 100644 --- a/src/ntfs-apply.c +++ b/src/ntfs-apply.c @@ -280,9 +280,9 @@ apply_file_attributes_and_security_data(ntfs_inode *ni, SACL_SECURITY_INFORMATION; ret = ntfs_set_inode_security(ni, selection, descriptor); #else - ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL, - ni, dir_ni, descriptor, - sd->sizes[dentry->security_id], 0); + ret = ntfs_xattr_system_setxattr(&ctx, XATTR_NTFS_ACL, + ni, dir_ni, descriptor, + sd->sizes[dentry->security_id], 0); #endif if (ret != 0) { diff --git a/src/security.c b/src/security.c index 6f0152ea..fef6043a 100644 --- a/src/security.c +++ b/src/security.c @@ -1,8 +1,9 @@ /* * security.c * - * Read the security data from the WIM. Doing anything with the security data - * is not yet implemented other than printing some information about it. + * Read and write the WIM security data. The security data is a table of + * security descriptors. Each WIM image has its own security data, but it's + * possible that an image's security data have no security descriptors. */ /* @@ -28,6 +29,29 @@ #include "io.h" #include "security.h" +/* + * This is a hack to work around a problem in libntfs-3g. libntfs-3g validates + * security descriptors with a function named ntfs_valid_descr(). + * ntfs_valid_descr() considers a security descriptor that ends in a SACL + * (Sysetm Access Control List) with no ACE's (Access Control Entries) to be + * invalid. However, a security descriptor like this exists in the Windows 7 + * install.wim. Here, security descriptors matching this pattern are modified + * to have no SACL. This should make no difference since the SACL had no + * entries anyway; however his ensures that that the security descriptors pass + * the validation in libntfs-3g. + */ +static void empty_sacl_fixup(char *descr, u64 *size_p) +{ + if (*size_p >= sizeof(SecurityDescriptor)) { + SecurityDescriptor *sd = (SecurityDescriptor*)descr; + u32 sacl_offset = le32_to_cpu(sd->sacl_offset); + if (sacl_offset == *size_p - sizeof(ACL)) { + sd->sacl_offset = to_le32(0); + *size_p -= sizeof(ACL); + } + } +} + /* * Reads the security data from the metadata resource. * @@ -150,6 +174,7 @@ int read_security_data(const u8 metadata_resource[], u64 metadata_resource_len, goto out_free_sd; } p = get_bytes(p, sd->sizes[i], sd->descriptors[i]); + empty_sacl_fixup(sd->descriptors[i], &sd->sizes[i]); } out: sd->total_length = (u32)total_len; -- 2.43.0 From 02ba077b44a9a78327408541da6c78c0238f114b Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 13:26:11 -0500 Subject: [PATCH 11/16] Update NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 78c8eb1c..56028c75 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,10 @@ Version 1.0.0: xattr or a Windows-style stream interface. Also they are supported when capturing a WIM from NTFS or applying a WIM to NTFS. + Split WIMs are better supported. You may now apply an image directly + from a split WIM, mount an image from a split WIM read-only, or export + an image from a split WIM. + Using a capture configuration file is now supported (but not fully yet). SHA1 message digests are checked in more places, so we can make sure -- 2.43.0 From 56a8c9b91ce1f759de62b920fa8f19435e17afb9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 13:26:36 -0500 Subject: [PATCH 12/16] Update TODO --- TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO b/TODO index 2e10e853..149d4e33 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ - Fix bugs - Add more test cases -- Better support for split WIMs - Implement LZX block splitting to improve compression ratio -- 2.43.0 From 5ab1d0645f63d09334d1b2573e36c033d078f4f2 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 13:49:57 -0500 Subject: [PATCH 13/16] doxygen docs updates --- src/wimlib.h | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/wimlib.h b/src/wimlib.h index 05681b3a..648345a5 100644 --- a/src/wimlib.h +++ b/src/wimlib.h @@ -387,7 +387,7 @@ enum wimlib_error_code { * Pointer to the contents of an image capture configuration file. If @c * NULL, a default string is used. Please see the manual page for * imagex capture for more information. - * @param config_size + * @param config_len * Length of the string @a config in bytes. * * @param flags @@ -435,7 +435,7 @@ extern int wimlib_add_image(WIMStruct *wim, const char *dir, * ::WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE may not be specified because we capture * the reparse points exactly as they are. */ -extern int wimlib_add_image_from_ntfs_volume(WIMStruct *w, const char *device, +extern int wimlib_add_image_from_ntfs_volume(WIMStruct *wim, const char *device, const char *name, const char *config, size_t config_len, @@ -450,7 +450,7 @@ extern int wimlib_add_image_from_ntfs_volume(WIMStruct *w, const char *device, * apply mode we apply the reparse points and hard links exactly as they are in * the WIM. */ -extern int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image, +extern int wimlib_apply_image_to_ntfs_volume(WIMStruct *wim, int image, const char *device, int flags, WIMStruct **additional_swms, unsigned num_additional_swms); @@ -520,8 +520,8 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * The image to export from @a src_wim. Can be the number of an image, or * ::WIM_ALL_IMAGES to export all images. * @param dest_wim - * Pointer to the ::WIMStruct for a WIM filethat will receive the images being - * exported. + * Pointer to the ::WIMStruct for a WIM file that will receive the images + * being exported. * @param dest_name * The name to give the exported image in the new WIM file. If left @c NULL, * the name from @a src_wim is used. This parameter must be left @c NULL @@ -552,7 +552,7 @@ extern int wimlib_delete_image(WIMStruct *wim, int image); * 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. * - * @return 0 on success; nonzero on error. On error, @dest_wim is left in an + * @return 0 on success; nonzero on error. On error, @a dest_wim is left in an * indeterminate state and should be freed with wimlib_free(). * @retval ::WIMLIB_ERR_DECOMPRESSION * Could not decompress the metadata resource for @a src_image @@ -952,10 +952,9 @@ extern int wimlib_mount(WIMStruct *wim, int image, const char *dir, int flags, * If ::WIMLIB_OPEN_FLAG_SHOW_PROGRESS is given, progress information will * be shown if the integrity of the WIM is checked. * If ::WIMLIB_OPEN_FLAG_SPLIT_OK is given, no error will be issued if the - * WIM is part of a split WIM. However, wimlib does not fully support - * split WIMs, so not all functions will work correctly after opening a - * split WIM. For example, you cannot use wimlib_mount() or - * wimlib_extract_image() on a split WIM. + * WIM is part of a split WIM; otherwise WIMLIB_ERR_SPLIT_UNSUPPORTED is + * returned. (This flag may be removed in the future, in which case no + * error will be issued when opening a split WIM.) * * @param wim_ret * On success, a pointer to an opaque ::WIMStruct for the opened WIM file @@ -1020,10 +1019,6 @@ extern int wimlib_open_wim(const char *wim_file, int flags, * file after it is has been completely written. The temporary file currently * is made in the same directory as the original WIM file. * - * Note that it is not possible for this function to delete the original file - * before having written the new file because it is very likely that file - * resources in the new WIM file need to be retrieved from the old WIM file. - * * After this function returns, @a wim must be freed using wimlib_free(). Any * further actions on @a wim before doing this are undefined. * @@ -1278,18 +1273,18 @@ extern int wimlib_set_image_descripton(WIMStruct *wim, int image, const char *description); /** - * Changes what is written in the element in the WIM XML data (something - * like "Core" or "Ultimate") + * Changes what is written in the \ element in the WIM XML data + * (something like "Core" or "Ultimate") * * @param wim * Pointer to the ::WIMStruct for a WIM file. It may be either a * standalone WIM or part of a split WIM; however, you should set the same - * element on all parts of a split WIM. + * \ element on all parts of a split WIM. * @param image * The number of the image for which to change the description. * @param flags - * The new element to give the image. It may be @c NULL, which - * indicates that the image is to be given no element. + * The new \ element to give the image. It may be @c NULL, which + * indicates that the image is to be given no \ element. * * @return 0 on success; nonzero on error. * @retval ::WIMLIB_ERR_INVALID_IMAGE @@ -1299,8 +1294,7 @@ extern int wimlib_set_image_descripton(WIMStruct *wim, int image, * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate the memory needed to duplicate the @a flags string. */ -extern int wimlib_set_image_flags(WIMStruct *w, int image, - const char *flags); +extern int wimlib_set_image_flags(WIMStruct *wim, int image, const char *flags); /** * Changes the name of an image in the WIM. @@ -1390,10 +1384,10 @@ extern int wimlib_set_print_errors(bool show_messages); * part. The other parts will have the same name with 2, 3, 4, ..., etc. * appended. * @param part_size - * The maximum size per part. It is not guaranteed that this will really - * be the maximum size per part, because some file resources in the WIM may - * be larger than this size, and the WIM file format provides no way to - * split up file resources among multiple WIMs. + * The maximum size per part, in bytes. It is not guaranteed that this + * will really be the maximum size per part, because some file resources in + * the WIM may be larger than this size, and the WIM file format provides + * no way to split up file resources among multiple WIMs. * @param flags * Bitwise OR of ::WIMLIB_OPEN_FLAG_CHECK_INTEGRITY and/or * ::WIMLIB_OPEN_FLAG_SHOW_PROGRESS. @@ -1504,7 +1498,7 @@ extern int wimlib_unmount(const char *dir, int flags); * @retval ::WIMLIB_ERR_INVALID_RESOURCE_SIZE * The metadata resource for @a image in @a wim is invalid. * @retval ::WIMLIB_ERR_INVALID_SECURITY_DATA - * The security data for @a image in @wim is invalid. + * The security data for @a image in @a wim is invalid. * @retval ::WIMLIB_ERR_NOMEM * Failed to allocate needed memory. * @retval ::WIMLIB_ERR_OPEN -- 2.43.0 From a05f5ab4d86d5c66896525186fd7482921d5ea9e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 14:27:16 -0500 Subject: [PATCH 14/16] Test suite: Allow out of directory build --- tests/test-imagex | 4 ++-- tests/test-imagex-ntfs | 31 ++++++++++++++++++------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/test-imagex b/tests/test-imagex index 2c729cb4..a0b88a96 100755 --- a/tests/test-imagex +++ b/tests/test-imagex @@ -3,8 +3,8 @@ # This script does some sanity testing of the 'imagex' program. It by no means # tests every aspect of wimlib comprehensively. -# Assume an in-tree build. set -e +srcdir=`realpath $srcdir` cd tests imagex() { @@ -24,7 +24,7 @@ rm -rf tmp || true # Make test directory mkdir dir -cp ../src/*.c ../src/*.h dir +cp $srcdir/src/*.c $srcdir/src/*.h dir mkdir dir/subdir echo 'hello' > dir/subdir/hello echo 'hello' > dir/subdir/hello2 diff --git a/tests/test-imagex-ntfs b/tests/test-imagex-ntfs index 6d7fbd5b..3b4d5a14 100755 --- a/tests/test-imagex-ntfs +++ b/tests/test-imagex-ntfs @@ -4,9 +4,12 @@ # checking the NTFS capture and apply features. # # This test will fail if wimlib was compiled with --without-ntfs-3g. +# +# Please note that cleanup is not done if a test fails, and NTFS volumes may +# remain mounted. -# Assume an in-tree build. set -e +srcdir=`realpath $srcdir` cd tests imagex() { @@ -56,7 +59,7 @@ init() { cleanup() { do_unmount in.mnt do_unmount out.mnt - rm -rf in.ntfs out.ntfs in.mnt out.mnt in.xattr out.xattr + rm -rf in.ntfs out.ntfs in.mnt out.mnt in.xattr out.xattr ntfs.wim } #trap cleanup exit @@ -209,27 +212,27 @@ do_test 'echo 999 > file; setfattr -v DOSNAME -n system.ntfs_dos_name file;' msg "NTFS volume containing C source code of wimlib" -do_test 'cp ../../src/*.c ../../src/*.h .' +do_test 'cp $srcdir/src/*.{c,h} .' msg "NTFS volume containing file with security descriptor" do_test 'touch file; - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` file' + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file' msg "NTFS volume containing files with different security descriptors" do_test 'touch file; touch file2; - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` file - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_2.base64` file' + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_2.base64` file' msg "NTFS volume containing files with different security descriptors and some with the same security descriptor" do_test 'touch file; touch file2; touch file3; mkdir dir; - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` file - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_2.base64` file - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` dir - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` file3' + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_2.base64` file + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` file3' msg "NTFS volume containing tons of random stuff" do_test 'echo -n 8 > file; @@ -239,7 +242,7 @@ do_test 'echo -n 8 > file; dd if=/dev/urandom of=randomfile bs=4096 count=10 &>/dev/null; mkdir dir; setfattr -n system.ntfs_dos_name -v DOSNAME dir; - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` dir + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir mkdir anotherdir; cp file anotherdir; ln file anotherdir/anotherhardlink; @@ -251,18 +254,20 @@ do_test 'echo -n 8 > file; setfattr -n user.ads3 anotherfile -v 33; echo -n > emptyfile; setfattr -n user.ads emptyfile -v 8; - setfattr -n user.ads5 emptyfile -v"`cat ../../src/hardlink.c`" + setfattr -n user.ads5 emptyfile -v"`cat $srcdir/src/hardlink.c`" mkdir dir/subdir; ln file dir/subdir/file; echo -n 8 > dir/subdir/file2; ln dir/subdir/file dir/subdir/link; setfattr -n system.ntfs_dos_name -v 123 dir/subdir/link; - setfattr -n system.ntfs_acl -v 0s`cat ../security_descriptor_1.base64` dir/subdir/link; + setfattr -n system.ntfs_acl -v 0s`cat $srcdir/tests/security_descriptor_1.base64` dir/subdir/link; setfattr -n user.yet_another_ads -v "" dir/subdir/link; setfattr -n user.yet_another_ads2 -v "" dir/subdir/link; setfattr -n user.yet_another_ads3 -v "abc" dir/subdir/link; setfattr -n user.yet_another_ads4 -v "" dir/subdir/link;' +cleanup + echo "**********************************************************" echo " NTFS capture/apply tests passed " echo "**********************************************************" -- 2.43.0 From 3a96515f1f442f03e26e08c3a7a410d659fb4d0d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 14:47:05 -0500 Subject: [PATCH 15/16] Update Arch Linux PKGBUILD --- archlinux/PKGBUILD.in | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/archlinux/PKGBUILD.in b/archlinux/PKGBUILD.in index 3dd48fcd..6c7ef08d 100644 --- a/archlinux/PKGBUILD.in +++ b/archlinux/PKGBUILD.in @@ -1,6 +1,6 @@ # Maintainer: Eric Biggers -# Set and $md5sums and delete this line to complete the PKGBUILD +# Add md5sum and delete this line to complete the PKGBUILD pkgname=wimlib pkgver=@VERSION@ @@ -9,22 +9,28 @@ pkgdesc="A library to extract, create, and modify WIM files" arch=("i686" "x86_64") url="http://sourceforge.net/projects/wimlib" license=("LGPL") -depends=("openssl" "fuse" "libxml2") +depends=("openssl" "fuse" "libxml2" "ntfs-3g" "attr") optdepends=("cdrkit: for making ISO image of Windows PE" "mtools: for making disk image of Windows PE" "syslinux: for making disk image of Windows PE" - "cabextract: for extracting Windows PE from the WAIK") + "cabextract: for extracting Windows PE from the WAIK" + "ntfsprogs: for making NTFS filesystems") +checkdepends=("ntfsprogs") source=("http://downloads.sourceforge.net/$pkgname/$pkgname-$pkgver.tar.gz") -md5sums=("") build() { cd "$pkgname-$pkgver" - ./configure --disable-verify-compression --with-libcrypto \ - --prefix=/usr + ./configure --disable-verify-compression --with-libcrypto --with-fuse + --with-ntfs-3g --enable-xattr --prefix=/usr make } +check() { + make check +} + package() { cd "$pkgname-$pkgver" make DESTDIR="$pkgdir" install } + -- 2.43.0 From 847bc380811280f46eb4652c61bb9d06790aa78e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 1 Sep 2012 14:51:08 -0500 Subject: [PATCH 16/16] mkwinpeimg.1 updates --- doc/mkwinpeimg.1.in | 55 ++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/doc/mkwinpeimg.1.in b/doc/mkwinpeimg.1.in index 0db9d6e0..05700cc4 100644 --- a/doc/mkwinpeimg.1.in +++ b/doc/mkwinpeimg.1.in @@ -8,13 +8,14 @@ mkwinpeimg \- Make a customized bootable image of Windows PE .SH DESCRIPTION \fBmkwinpeimg\fR is able to make a bootable image of Windows PE by taking files -from a mounted Windows DVD (Windows 7 or Windows 8) or the mounted ISO image for -the Windows Automated Installation Kit (WAIK). The \fB--windows-dir\fR and -\fB--waik-dir\fR options are used to specify the locations of these mounted -ISOs. You only need one or the other. The files that \fBmkwinpeimg\fR will -retrieve are \fIboot.wim\fR, \fIbootmgr\fR, \fIboot.sdi\fR, and \fIbcd\fR. If -making an ISO image, the file \fIetfsboot.com\fR is also retrieved. Microsoft -owns the rights to these files and they are not distributed with WIMLIB. +from a mounted Windows DVD (Windows Vista, Windows 7 or Windows 8) or the +mounted ISO image for the Windows Automated Installation Kit (WAIK). The +\fB--windows-dir\fR and \fB--waik-dir\fR options are used to specify the +locations of these mounted ISOs. You only need one or the other. The files +that \fBmkwinpeimg\fR will retrieve are \fIboot.wim\fR, \fIbootmgr\fR, +\fIboot.sdi\fR, and \fIbcd\fR. If making an ISO image, the file +\fIetfsboot.com\fR is also retrieved. Microsoft owns the rights to these files +and they are not distributed with wimlib. \fBmkwinpeimg\fR can currently make two types of bootable images. The default is to make a bootable disk image. The image is not partitioned and is formatted @@ -75,8 +76,7 @@ directory, or F1_WINPE.WIM from the WAIK if \fB\-\-waik\-dir\fR is specified. Adds all the files in DIR to the Windows PE image. .TP \fB\-t\fR, \fB\-\-tmp\-dir\fR=\fIDIR\fR -Use DIR for temporary files. Defaults to a directory made using \e"mktemp -\fB\-d\fR\e". +Use DIR for temporary files. Defaults to a directory made using "mktemp -d". .TP \fB\-h\fR, \fB\-\-help\fR Display help. @@ -85,26 +85,35 @@ Display help. Show version information. .SH EXAMPLES -.IP +Create a bootable disk image of Windows PE from the Windows Vista, 7, or 8 +installation media mounted on /media/windows: + +.RS +.PP mkwinpeimg --windows-dir=/media/windows winpe.img -.LP -Create a bootable disk image of Windows PE from the Windows DVD mounted on -/media/windows. +.RE +.PP -.IP -mkwinpeimg --iso --waik-dir=/media/waik --overlay=winpe_overlay winpe.iso -.LP Create a bootable ISO of Windows PE from the WAIK mounted on /media/waik, and -add all the files in "winpe_overlay" to Windows PE's filesystem. +add all the files in "winpe_overlay" to Windows PE's filesystem: + +.RS +.PP +mkwinpeimg --iso --waik-dir=/media/waik --overlay=winpe_overlay winpe.iso +.RE +.PP + +Create a bootable image of Windows PE from the Windows installation media +mounted on /media/windows, add and make it execute "install.cmd" when it starts +up. In this example the image is created in the root directory of the TFTP +server for network booting. -.IP +.RS +.PP mkwinpeimg --start-script=install.cmd --windows-dir=/media/windows /var/tftpboot/winpe.img -.LP +.RE +.PP -Create a bootable image of Windows PE from the Windows DVD mounted on -/media/windows, add and make it execute "install.cmd" when it starts up. In -this example the image is created in the root directory of the TFTP server for -network booting. .SH NOTES -- 2.43.0