stbuf->st_size = 0;
stbuf->st_nlink = dentry_link_group_size(dentry);
+ stbuf->st_ino = dentry->hard_link;
stbuf->st_uid = getuid();
stbuf->st_gid = getgid();
stbuf->st_atime = ms_timestamp_to_unix(dentry->last_access_time);
return NULL;
}
-static void dentry_free_ads_entries(struct dentry *dentry)
+void dentry_free_ads_entries(struct dentry *dentry)
{
for (u16 i = 0; i < dentry->num_ads; i++)
destroy_ads_entry(&dentry->ads_entries[i]);
FREE(dentry);
}
+/* clones a dentry.
+ *
+ * Beware:
+ * - memory for file names is not cloned
+ * - next, prev, and children pointers and not touched
+ * - stream entries are not cloned.
+ */
+struct dentry *clone_dentry(struct dentry *old)
+{
+ struct dentry *new = MALLOC(sizeof(struct dentry));
+ if (!new)
+ return NULL;
+ return memcpy(new, old, sizeof(struct dentry));
+}
+
/* Arguments for do_free_dentry(). */
struct free_dentry_args {
struct lookup_table *lookup_table;
*/
if (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
/* ??? */
- u32 u1, u2;
- p = get_u32(p, &u1);
- /*p += 4;*/
+ p += 4;
p = get_u32(p, &dentry->reparse_tag);
- p = get_u32(p, &u2);
- /*p += 4;*/
- dentry->hard_link = (u64)(u1) | ((u64)(u2) << 32);
+ p += 4;
} else {
p = get_u32(p, &dentry->reparse_tag);
p = get_u64(p, &dentry->hard_link);
{
u8 *orig_p = p;
unsigned padding;
- memset(p, 0, dentry->length);
+
p = put_u64(p, dentry->length);
p = put_u32(p, dentry->attributes);
p = put_u32(p, dentry->security_id);
p = put_u64(p, dentry->creation_time);
p = put_u64(p, dentry->last_access_time);
p = put_u64(p, dentry->last_write_time);
- memcpy(p, dentry->hash, WIM_HASH_SIZE);
- p += WIM_HASH_SIZE;
- p = put_u32(p, dentry->reparse_tag);
- p = put_u64(p, dentry->hard_link);
- p = put_u16(p, dentry->num_ads); /*streams */
+ p = put_bytes(p, WIM_HASH_SIZE, dentry->hash);
+ if (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ p = put_zeroes(p, 4);
+ p = put_u32(p, dentry->reparse_tag);
+ p = put_zeroes(p, 4);
+ } else {
+ p = put_u32(p, dentry->reparse_tag);
+ p = put_u64(p, dentry->hard_link);
+ }
+ p = put_u16(p, dentry->num_ads);
p = put_u16(p, dentry->short_name_len);
p = put_u16(p, dentry->file_name_len);
p = put_bytes(p, dentry->file_name_len, (u8*)dentry->file_name);
/* write end of directory entry */
p = put_u64(p, 0);
} else {
- /* Nothing to do for a regular file. */
- if (dentry_is_regular_file(tree))
+ /* Nothing to do for non-directories */
+ if (!dentry_is_directory(tree))
return p;
}
dentry->children = first_child;
return ret;
}
-
-int dentry_set_symlink_buf(struct dentry *dentry, const u8 symlink_buf_hash[])
-{
- struct ads_entry *ads_entries;
-
- ads_entries = CALLOC(2, sizeof(struct ads_entry));
- if (!ads_entries)
- return WIMLIB_ERR_NOMEM;
- memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
- dentry_free_ads_entries(dentry);
- dentry->num_ads = 2;
- dentry->ads_entries = ads_entries;
- return 0;
-}
extern void init_dentry(struct dentry *dentry, const char *name);
extern struct dentry *new_dentry(const char *name);
+extern void dentry_free_ads_entries(struct dentry *dentry);
extern void free_dentry(struct dentry *dentry);
+extern struct dentry *clone_dentry(struct dentry *old);
extern void free_dentry_tree(struct dentry *root,
struct lookup_table *lookup_table,
bool lt_decrement_refcnt);
extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p);
-extern int dentry_set_symlink_buf(struct dentry *dentry,
- const u8 symlink_buf_hash[]);
/* Inline utility functions for WIMDentries */
static inline u8 *put_zeroes(u8 *p, size_t num_bytes)
{
- memset(p, 0, num_bytes);
- return p + num_bytes;
+ return (u8*)memset(p, 0, num_bytes) + num_bytes;
}
static inline u8 *put_bytes(u8 *p, size_t num_bytes, const u8 *input)
{
- memcpy(p, input, num_bytes);
- return p + num_bytes;
+ return (u8*)memcpy(p, input, num_bytes) + num_bytes;
}
#endif /* _WIMLIB_IO_H */
}
deref_name_buf[ret] = '\0';
DEBUG("Read symlink `%s'", deref_name_buf);
- void *symlink_buf = make_symlink_reparse_data_buf(deref_name_buf,
- &symlink_buf_len);
- if (!symlink_buf)
- return WIMLIB_ERR_NOMEM;
- DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
- symlink_buf_len, ret);
-
- u8 symlink_buf_hash[WIM_HASH_SIZE];
- sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
-
- ret = dentry_set_symlink_buf(root, symlink_buf_hash);
-
- if (ret != 0) {
- FREE(symlink_buf);
- return ret;
- }
- DEBUG("Created symlink buf");
-
- struct lookup_table_entry *lte;
- struct lookup_table_entry *existing_lte;
-
- existing_lte = __lookup_resource(lookup_table, symlink_buf_hash);
- if (existing_lte) {
- existing_lte->refcnt++;
- } else {
- DEBUG("Creating new lookup table entry");
- lte = new_lookup_table_entry();
- if (!lte) {
- FREE(symlink_buf);
- return WIMLIB_ERR_NOMEM;
- }
- lte->symlink_buf = symlink_buf;
- lte->resource_entry.original_size = symlink_buf_len;
- lte->resource_entry.size = symlink_buf_len;
- lte->is_symlink = true;
- memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
- lookup_table_insert(lookup_table, lte);
- }
+ ret = dentry_set_symlink(root, deref_name_buf, lookup_table);
} else {
/* Regular file */
struct lookup_table_entry *lte;
close_message_queues();
}
+static int wimfs_fgetattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ struct wimlib_fd *fd = (struct wimlib_fd*)fi->fh;
+ dentry_to_stbuf(fd->dentry, stbuf, w->lookup_table);
+ return 0;
+}
+
static int wimfs_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
return 0;
}
+/* Create a hard link */
+static int wimfs_link(const char *to, const char *from)
+{
+ struct dentry *to_dentry, *from_dentry, *from_dentry_parent;
+ const char *link_name;
+
+ to_dentry = get_dentry(w, to);
+ if (!to_dentry)
+ return -ENOENT;
+ if (!dentry_is_regular_file(to_dentry))
+ return -EPERM;
+
+ from_dentry_parent = get_parent_dentry(w, from);
+ if (!from_dentry_parent)
+ return -ENOENT;
+ if (!dentry_is_directory(from_dentry_parent))
+ return -ENOTDIR;
+
+ link_name = path_basename(from);
+ if (get_dentry_child_with_name(from_dentry_parent, link_name))
+ return -EEXIST;
+ from_dentry = clone_dentry(to_dentry);
+ if (!from_dentry)
+ return -ENOMEM;
+ if (change_dentry_name(from_dentry, link_name) != 0) {
+ FREE(from_dentry);
+ return -ENOMEM;
+ }
+ if (calculate_dentry_full_path(from_dentry, to_dentry) != 0) {
+ FREE(from_dentry->file_name);
+ FREE(from_dentry->file_name_utf8);
+ FREE(from_dentry);
+ return -ENOMEM;
+ }
+ list_add(&from_dentry->link_group_list, &to_dentry->link_group_list);
+ link_dentry(from_dentry, from_dentry_parent);
+ return 0;
+}
+
/*
* Create a directory in the WIM.
* @mode is currently ignored.
return 0;
}
+static int wimfs_symlink(const char *to, const char *from)
+{
+ struct dentry *dentry_parent, *dentry;
+ const char *link_name;
+
+ dentry_parent = get_parent_dentry(w, from);
+ if (!dentry_parent)
+ return -ENOENT;
+ if (!dentry_is_directory(dentry_parent))
+ return -ENOTDIR;
+
+ link_name = path_basename(from);
+
+ if (get_dentry_child_with_name(dentry_parent, link_name))
+ return -EEXIST;
+ dentry = new_dentry(link_name);
+ if (!dentry)
+ return -ENOMEM;
+
+ if (!change_dentry_name(dentry, link_name)) {
+ FREE(dentry);
+ return -ENOMEM;
+ }
+
+ if (calculate_dentry_full_path(dentry, NULL) != 0)
+ goto out_free_dentry;
+
+ dentry->attributes = FILE_ATTRIBUTE_REPARSE_POINT;
+ dentry->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+
+ if (dentry_set_symlink(dentry, to, w->lookup_table) != 0)
+ goto out_free_dentry;
+
+ link_dentry(dentry, dentry_parent);
+ return 0;
+out_free_dentry:
+ free_dentry(dentry);
+ return -ENOMEM;
+}
+
/* Reduce the size of a file */
static int wimfs_truncate(const char *path, off_t size)
}
-static struct fuse_operations wimfs_oper = {
+static struct fuse_operations wimfs_operations = {
.access = wimfs_access,
.destroy = wimfs_destroy,
+ .fgetattr = wimfs_fgetattr,
.ftruncate = wimfs_ftruncate,
.getattr = wimfs_getattr,
+ .link = wimfs_link,
.mkdir = wimfs_mkdir,
.mknod = wimfs_mknod,
.open = wimfs_open,
.release = wimfs_release,
.rename = wimfs_rename,
.rmdir = wimfs_rmdir,
+ .symlink = wimfs_symlink,
.truncate = wimfs_truncate,
.unlink = wimfs_unlink,
.utimens = wimfs_utimens,
w = wim;
mount_flags = flags;
- ret = fuse_main(argc, argv, &wimfs_oper, NULL);
+ ret = fuse_main(argc, argv, &wimfs_operations, NULL);
return (ret == 0) ? 0 : WIMLIB_ERR_FUSE;
}
if (!lte)
return 0;
- /* No need to write file resources twice. (This indicates file
- * resources that are part of a hard link set.) */
+ /* No need to write file resources twice. */
if (++lte->out_refcnt != 1)
return 0;
len = lte->resource_entry.original_size;
- recompress_resource(in_fp, lte->symlink_buf, len, len, 0,
- 0, out_fp, out_wim_ctype, &new_size);
+ ret = recompress_resource(NULL, lte->symlink_buf, len, len, 0,
+ 0, out_fp, out_wim_ctype, &new_size);
output_res_entry->size = new_size;
output_res_entry->original_size = len;
output_res_entry->offset = offset;
#include "dentry.h"
#include "io.h"
#include "lookup_table.h"
+#include "sha1.h"
/*
* Find the symlink target of a symbolic link or junction point in the WIM.
*/
static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
char *buf, size_t buf_len,
- bool is_junction_point)
+ u32 reparse_tag)
{
const u8 *p = resource;
u16 substitute_name_offset;
unsigned header_size;
char *translated_target;
bool is_absolute;
+ u32 flags;
if (resource_len < 12)
return -EIO;
p = get_u16(p, &substitute_name_len);
p = get_u16(p, &print_name_offset);
p = get_u16(p, &print_name_len);
- if (is_junction_point) {
+ get_u32(p, &flags);
+
+ wimlib_assert(reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+ reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+
+ /* I think that some junction points incorrectly get marked as symbolic
+ * links. So, parse the link buffer as a symlink if the flags seem
+ * plausible. */
+ if (flags <= 1)
+ reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+
+ if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT) {
header_size = 8;
} else {
- u32 flags;
- p = get_u32(p, &flags);
is_absolute = (flags & 1) ? false : true;
header_size = 12;
+ p += 4;
}
if (header_size + substitute_name_offset + substitute_name_len > resource_len)
return -EIO;
}
translated_target = link_target;
- if (is_junction_point || is_absolute) {
+ if (reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT || is_absolute) {
if (link_target_len < 7
|| memcmp(translated_target, "\\??\\", 4) != 0
|| translated_target[4] == '\0'
}
translated_target += 4;
link_target_len -= 4;
+ /* There's a drive letter, so just leave the backslashes since
+ * it won't go anyhwere on UNIX anyway... */
+ } else {
+ for (size_t i = 0; i < link_target_len; i++)
+ if (translated_target[i] == '\\')
+ translated_target[i] = '/';
}
- for (size_t i = 0; i < link_target_len; i++)
- if (translated_target[i] == '\\')
- translated_target[i] = '/';
memcpy(buf, translated_target, link_target_len + 1);
ret = link_target_len;
ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
const WIMStruct *w)
{
- struct ads_entry *ads;
- struct lookup_table_entry *entry;
struct resource_entry *res_entry;
- bool is_junction_point;
+ struct lookup_table_entry *lte;
+ u16 i = 0;
+ const u8 *hash = dentry->hash;
wimlib_assert(dentry_is_symlink(dentry));
- if (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) {
- is_junction_point = false;
- /*
- * 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)
+ while (1) {
+ if ((lte = __lookup_resource(w->lookup_table, hash)))
+ break;
+ if (i == dentry->num_ads)
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;
- } else {
- wimlib_assert(dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
-
- is_junction_point = true;
-
- if ((entry = __lookup_resource(w->lookup_table, dentry->hash)))
- goto do_readlink;
+ hash = dentry->ads_entries[i].hash;
+ i++;
}
- return -EIO;
-do_readlink:
- res_entry = &entry->resource_entry;
+ res_entry = <e->resource_entry;
if (res_entry->original_size > 10000)
return -EIO;
+
char res_buf[res_entry->original_size];
if (read_full_resource(w->fp, res_entry->size,
res_entry->original_size,
res_buf) != 0)
return -EIO;
return get_symlink_name(res_buf, res_entry->original_size, buf,
- buf_len, is_junction_point);
+ buf_len, dentry->reparse_tag);
+}
+
+static int dentry_set_symlink_buf(struct dentry *dentry,
+ const u8 symlink_buf_hash[])
+{
+ struct ads_entry *ads_entries;
+
+ ads_entries = CALLOC(2, sizeof(struct ads_entry));
+ if (!ads_entries)
+ return WIMLIB_ERR_NOMEM;
+ memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
+ dentry_free_ads_entries(dentry);
+ dentry->num_ads = 2;
+ dentry->ads_entries = ads_entries;
+ return 0;
+}
+
+int dentry_set_symlink(struct dentry *dentry, const char *target,
+ struct lookup_table *lookup_table)
+
+{
+ int ret;
+ size_t symlink_buf_len;
+ struct lookup_table_entry *lte = NULL, *existing_lte;
+ u8 symlink_buf_hash[WIM_HASH_SIZE];
+ void *symlink_buf;
+
+ symlink_buf = make_symlink_reparse_data_buf(target, &symlink_buf_len);
+ if (!symlink_buf)
+ return WIMLIB_ERR_NOMEM;
+
+ DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
+ symlink_buf_len, ret);
+
+ sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
+
+ existing_lte = __lookup_resource(lookup_table, symlink_buf_hash);
+
+ if (existing_lte) {
+ existing_lte->refcnt++;
+ } else {
+ DEBUG("Creating new lookup table entry for symlink buf");
+ lte = new_lookup_table_entry();
+ if (!lte) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_symlink_buf;
+ }
+ lte->is_symlink = true;
+ lte->symlink_buf = symlink_buf;
+ lte->resource_entry.original_size = symlink_buf_len;
+ lte->resource_entry.size = symlink_buf_len;
+ memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
+ }
+
+ ret = dentry_set_symlink_buf(dentry, symlink_buf_hash);
+
+ if (ret != 0)
+ goto out_free_lte;
+
+ DEBUG("Loaded symlink buf");
+
+ if (!existing_lte)
+ lookup_table_insert(lookup_table, lte);
+ return 0;
+out_free_lte:
+ FREE(lte);
+out_free_symlink_buf:
+ FREE(symlink_buf);
+ return ret;
}
const WIMStruct *w);
extern void *make_symlink_reparse_data_buf(const char *symlink_target,
size_t *len_ret);
+extern int dentry_set_symlink(struct dentry *dentry,
+ const char *target,
+ struct lookup_table *lookup_table);
/* wim.c */
extern WIMStruct *new_wim_struct();