From 0e4862eb6d10b7266240fe29ed1ecbd2ad5b0f36 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Sat, 18 Aug 2012 01:31:23 -0500 Subject: [PATCH] Implement readlink() on WIM filesystem --- src/dentry.c | 4 ++- src/dentry.h | 30 ++++++++++-------- src/mount.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 16 deletions(-) diff --git a/src/dentry.c b/src/dentry.c index 56a06041..a25680f9 100644 --- a/src/dentry.c +++ b/src/dentry.c @@ -64,7 +64,9 @@ void dentry_to_stbuf(const struct dentry *dentry, struct stat *stbuf, { struct lookup_table_entry *lte; - if (dentry_is_directory(dentry)) + if (dentry_is_symlink(dentry)) + stbuf->st_mode = S_IFLNK | 0777; + else if (dentry_is_directory(dentry)) stbuf->st_mode = S_IFDIR | 0755; else stbuf->st_mode = S_IFREG | 0644; diff --git a/src/dentry.h b/src/dentry.h index e9fef475..6c260fc2 100644 --- a/src/dentry.h +++ b/src/dentry.h @@ -10,25 +10,23 @@ #define WIM_ADS_ENTRY_DISK_SIZE 38 -#ifndef WITH_NTFS_3G /* * Reparse tags documented at * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx * * IO_REPARSE_TAG_SYMLINK is the only one we really care about. */ -#define IO_REPARSE_TAG_RESERVED_ZERO 0x00000000 -#define IO_REPARSE_TAG_RESERVED_ONE 0x00000001 -#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 -#define IO_REPARSE_TAG_HSM 0xC0000004 -#define IO_REPARSE_TAG_HSM2 0x80000006 -#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 -#define IO_REPARSE_TAG_SIS 0x80000007 -#define IO_REPARSE_TAG_DFS 0x8000000A -#define IO_REPARSE_TAG_DFSR 0x80000012 -#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B -#define IO_REPARSE_TAG_SYMLINK 0xA000000C -#endif /* !WITH_NTFS_3G */ +#define WIM_IO_REPARSE_TAG_RESERVED_ZERO 0x00000000 +#define WIM_IO_REPARSE_TAG_RESERVED_ONE 0x00000001 +#define WIM_IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 +#define WIM_IO_REPARSE_TAG_HSM 0xC0000004 +#define WIM_IO_REPARSE_TAG_HSM2 0x80000006 +#define WIM_IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 +#define WIM_IO_REPARSE_TAG_SIS 0x80000007 +#define WIM_IO_REPARSE_TAG_DFS 0x8000000A +#define WIM_IO_REPARSE_TAG_DFSR 0x80000012 +#define WIM_IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B +#define WIM_IO_REPARSE_TAG_SYMLINK 0xA000000C #define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 @@ -277,6 +275,12 @@ static inline bool dentry_is_directory(const struct dentry *dentry) return (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } +static inline bool dentry_is_symlink(const struct dentry *dentry) +{ + return (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) + && (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK); +} + static inline bool dentry_is_regular_file(const struct dentry *dentry) { return !dentry_is_directory(dentry); diff --git a/src/mount.c b/src/mount.c index c6ce5588..a4359947 100644 --- a/src/mount.c +++ b/src/mount.c @@ -32,6 +32,7 @@ #include "sha1.h" #include "lookup_table.h" #include "xml.h" +#include "io.h" #include "timestamp.h" #include #include @@ -772,14 +773,96 @@ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, return 0; do { - memset(&st, 0, sizeof(st)); - if (filler(buf, child->file_name_utf8, &st, 0)) + if (filler(buf, child->file_name_utf8, NULL, 0)) return 0; child = child->next; } while (child != parent->children); return 0; } +/* + * Find the symlink target of a symbolic link dentry in the WIM. + * + * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx + * Except the first 8 bytes aren't included in the resource (presumably because + * we already know the reparse tag from the dentry, and we already know the + * reparse tag len from the lookup table entry resource length). + */ +static int get_symlink_name(const u8 *resource, size_t resource_len, + char *buf, size_t buf_len) +{ + const u8 *p = resource; + u16 substitute_name_offset; + u16 substitute_name_len; + u16 print_name_offset; + u16 print_name_len; + u32 flags; + char *link_target; + size_t link_target_len; + int ret; + if (resource_len < 12) + return -EIO; + p = get_u16(p, &substitute_name_offset); + p = get_u16(p, &substitute_name_len); + p = get_u16(p, &print_name_offset); + p = get_u16(p, &print_name_len); + p = get_u32(p, &flags); + if (12 + substitute_name_offset + substitute_name_len > resource_len) + return -EIO; + link_target = utf16_to_utf8(p + substitute_name_offset, + substitute_name_len, + &link_target_len); + if (!link_target) + return -EIO; + if (link_target_len + 1 > buf_len) { + ret = -ENAMETOOLONG; + goto out; + } + memcpy(buf, link_target, link_target_len + 1); + ret = 0; +out: + free(link_target); + return ret; +} + +static int wimfs_readlink(const char *path, char *buf, size_t buf_len) +{ + struct dentry *dentry = get_dentry(w, path); + struct ads_entry *ads; + struct lookup_table_entry *entry; + struct resource_entry *res_entry; + if (!dentry) + return -ENOENT; + if (!dentry_is_symlink(dentry)) + return -EINVAL; + + /* + * This is of course not actually documented, but what I think is going + * on here is that the symlink dentries have 2 alternate data streams; + * one is the default data stream, which is not used and is empty, and + * one is the symlink buffer data stream, which is confusingly also + * unnamed, but isn't empty as it contains the symlink target within the + * resource. + */ + if (dentry->num_ads != 2) + return -EIO; + if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[0].hash))) + goto do_readlink; + if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[1].hash))) + goto do_readlink; + return -EIO; +do_readlink: + res_entry = &entry->resource_entry; + char res_buf[res_entry->original_size]; + if (read_full_resource(w->fp, res_entry->size, + res_entry->original_size, + res_entry->offset, + wim_resource_compression_type(w, res_entry), + res_buf) != 0) + return -EIO; + return get_symlink_name(res_buf, res_entry->original_size, buf, buf_len); +} + /* Close a file. */ static int wimfs_release(const char *path, struct fuse_file_info *fi) { @@ -1070,6 +1153,7 @@ static struct fuse_operations wimfs_oper = { .opendir = wimfs_opendir, .read = wimfs_read, .readdir = wimfs_readdir, + .readlink = wimfs_readlink, .release = wimfs_release, .rename = wimfs_rename, .rmdir = wimfs_rmdir, -- 2.43.0