#include "sha1.h"
#include "lookup_table.h"
#include "xml.h"
+#include "io.h"
+#include "timestamp.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
staging_dir_name = MALLOC(staging_dir_name_len + 1);
if (!staging_dir_name) {
- ERROR("Out of memory!\n");
+ ERROR("Out of memory");
return;
}
staging_dir_name[staging_dir_name_len] = '\0';
if (mkdir(staging_dir_name, 0700) != 0) {
- ERROR("Failed to create temporary directory `%s': %m\n",
- staging_dir_name);
+ ERROR_WITH_ERRNO("Failed to create temporary directory `%s'",
+ staging_dir_name);
FREE(staging_dir_name);
staging_dir_name = NULL;
}
*/
static inline int delete_staging_dir()
{
- return nftw(staging_dir_name, remove_file_or_directory, 10, FTW_DEPTH);
+ int ret;
+
+ ret = nftw(staging_dir_name, remove_file_or_directory,10, FTW_DEPTH);
+ staging_dir_name = NULL;
+ return ret;
}
/* Name and message queue descriptors for message queues between the filesystem
unmount_to_daemon_mq_name = strcat_dup(slash, mount_dir_basename,
prefix, u2d_suffix);
if (!unmount_to_daemon_mq_name) {
- ERROR("Out of memory!\n");
+ ERROR("Out of memory");
return WIMLIB_ERR_NOMEM;
}
daemon_to_unmount_mq_name = strcat_dup(slash, mount_dir_basename,
prefix, d2u_suffix);
if (!daemon_to_unmount_mq_name) {
- ERROR("Out of memory!\n");
+ ERROR("Out of memory");
ret = WIMLIB_ERR_NOMEM;
goto err1;
}
0700, NULL);
if (unmount_to_daemon_mq == -1) {
- ERROR("mq_open(): %m\n");
+ ERROR_WITH_ERRNO("mq_open()");
ret = WIMLIB_ERR_MQUEUE;
goto err2;
}
0700, NULL);
if (daemon_to_unmount_mq == -1) {
- ERROR("mq_open(): %m\n");
+ ERROR_WITH_ERRNO("mq_open()");
ret = WIMLIB_ERR_MQUEUE;
goto err3;
}
if (mq_getattr(unmount_to_daemon_mq, &attr) == 0) {
msgsize = attr.mq_msgsize;
} else {
- ERROR("mq_getattr(): %m\n");
- ERROR("Attempting to read %s\n", msgsize_max_file);
+ ERROR_WITH_ERRNO("mq_getattr()");
+ ERROR("Attempting to read %s", msgsize_max_file);
fp = fopen(msgsize_max_file, "rb");
if (fp) {
if (fscanf(fp, "%d", &msgsize) != 1) {
- ERROR("Assuming message size of 8192\n");
+ ERROR("Assuming message size of 8192");
msgsize = 8192;
}
fclose(fp);
} else {
- ERROR("Failed to open file %s: %m\n",
- msgsize_max_file);
- ERROR("Assuming message size of 8192\n");
+ ERROR_WITH_ERRNO("Failed to open the file `%s'",
+ msgsize_max_file);
+ ERROR("Assuming message size of 8192");
msgsize = 8192;
}
}
{
if (lte->staging_file_name && lte->staging_num_times_opened) {
if (close(lte->staging_fd) != 0) {
- ERROR("Failed close file `%s': %m\n",
- lte->staging_file_name);
+ ERROR_WITH_ERRNO("Failed close file `%s'",
+ lte->staging_file_name);
return WIMLIB_ERR_WRITE;
}
}
if (lte && lte->staging_file_name) {
- DEBUG("Calculating SHA1 hash for file `%s'\n", dentry->file_name_utf8);
+ DEBUG("Calculating SHA1 hash for file `%s'",
+ dentry->file_name_utf8);
ret = sha1sum(lte->staging_file_name, dentry->hash);
if (ret != 0)
return ret;
memcpy(lte->hash, dentry->hash, WIM_HASH_SIZE);
existing = lookup_resource(table, dentry->hash);
if (existing) {
- DEBUG("Merging duplicate lookup table entries for "
- "file `%s'\n", dentry->file_name_utf8);
+ DEBUG("Merging duplicate lookup table entries for file "
+ "`%s'", dentry->file_name_utf8);
free_lookup_table_entry(lte);
existing->refcnt++;
} else {
root = wim_root_dentry(w);
- DEBUG("Closing all staging file descriptors.\n");
+ DEBUG("Closing all staging file descriptors.");
/* Close all the staging file descriptors. */
- ret = for_lookup_table_entry(w->lookup_table,
- close_staging_file, NULL);
+ ret = for_lookup_table_entry(w->lookup_table, close_staging_file, NULL);
if (ret != 0) {
- ERROR("Failed to close all staging files!\n");
+ ERROR("Failed to close all staging files");
return ret;
}
- DEBUG("Calculating SHA1 checksums for all new staging files.\n");
+ DEBUG("Calculating SHA1 checksums for all new staging files.");
/* Calculate SHA1 checksums for all staging files, and merge unnecessary
* lookup table entries. */
ret = for_dentry_in_tree(root, calculate_sha1sum_for_staging_file,
w->lookup_table);
if (ret != 0) {
- ERROR("Failed to calculate new SHA1 checksums!\n");
+ ERROR("Failed to calculate new SHA1 checksums");
return ret;
}
ret = wimlib_overwrite(w, check_integrity);
if (ret != 0) {
- ERROR("Failed to commit changes\n");
+ ERROR("Failed to commit changes");
return ret;
}
return ret;
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + 3;
timeout.tv_nsec = now.tv_usec * 1000;
- DEBUG("Waiting for message telling us whether to commit or not, "
- "and whether to include integrity checks.\n");
+ DEBUG("Waiting for message telling us whether to commit or not, and "
+ "whether to include integrity checks.");
bytes_received = mq_timedreceive(unmount_to_daemon_mq, msg,
msgsize, NULL, &timeout);
check_integrity = msg[1];
if (bytes_received == -1) {
if (errno == ETIMEDOUT) {
- ERROR("Timed out.\n");
+ ERROR("Timed out.");
} else {
- ERROR("mq_timedreceive(): %m\n");
+ ERROR_WITH_ERRNO("mq_timedreceive()");
}
- ERROR("Not committing.\n");
+ ERROR("Not committing.");
} else {
- DEBUG("Received message: [%d %d]\n", msg[0], msg[1]);
+ DEBUG("Received message: [%d %d]", msg[0], msg[1]);
}
status = 0;
if (commit) {
status = chdir(working_directory);
if (status != 0) {
- ERROR("chdir(): %m\n");
+ ERROR_WITH_ERRNO("chdir()");
status = WIMLIB_ERR_NOTDIR;
goto done;
}
}
ret = delete_staging_dir();
if (ret != 0) {
- ERROR("Failed to delete the staging directory: %m\n");
+ ERROR_WITH_ERRNO("Failed to delete the staging "
+ "directory");
if (status == 0)
status = ret;
}
done:
ret = mq_send(daemon_to_unmount_mq, &status, 1, 1);
if (ret == -1)
- ERROR("Failed to send status to unmount process: %m\n");
+ ERROR_WITH_ERRNO("Failed to send status to unmount process");
close_message_queues();
}
return -EEXIST;
newdir = new_dentry(basename);
- newdir->attributes |= WIM_FILE_ATTRIBUTE_DIRECTORY;
+ newdir->attributes |= FILE_ATTRIBUTE_DIRECTORY;
link_dentry(newdir, parent);
return 0;
}
/* doesn't exist--- ok */
}
- DEBUG("Creating staging file '%s'\n", name);
+ DEBUG("Creating staging file '%s'", name);
fd = creat(name, 0600);
if (fd == -1) {
/* Create a lookup table entry having the same hash value */
lte = new_lookup_table_entry();
- lte->staging_num_times_opened = 0;
- lte->resource_entry.original_size = 0;
memcpy(lte->hash, dentry->hash, WIM_HASH_SIZE);
fd = create_staging_file(&tmpfile_name);
}
} else {
/* no lookup table entry, so the file must be empty. Create a
- * lookup table entry for the file. */
+ * lookup table entry for the file, unless it's a read-only
+ * filesystem. */
char *tmpfile_name;
int fd;
+ if (!staging_dir_name) /* Read-only filesystem */
+ return 0;
+
lte = new_lookup_table_entry();
if (!lte)
return -ENOMEM;
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)
{
return 0;
}
+static int wimfs_utimens(const char *path, const struct timespec tv[2])
+{
+ struct dentry *dentry = get_dentry(w, path);
+ if (!dentry)
+ return -ENOENT;
+ time_t last_access_t = (tv[0].tv_nsec == UTIME_NOW) ?
+ time(NULL) : tv[0].tv_sec;
+ dentry->last_access_time = unix_timestamp_to_ms(last_access_t);
+ time_t last_mod_t = (tv[1].tv_nsec == UTIME_NOW) ?
+ time(NULL) : tv[1].tv_sec;
+ dentry->last_write_time = unix_timestamp_to_ms(last_mod_t);
+ return 0;
+}
+
/* Writes to a file in the WIM filesystem. */
static int wimfs_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
.opendir = wimfs_opendir,
.read = wimfs_read,
.readdir = wimfs_readdir,
+ .readlink = wimfs_readlink,
.release = wimfs_release,
.rename = wimfs_rename,
.rmdir = wimfs_rmdir,
.truncate = wimfs_truncate,
.unlink = wimfs_unlink,
+ .utimens = wimfs_utimens,
.write = wimfs_write,
};
if (flags & WIMLIB_MOUNT_FLAG_READWRITE)
wim_get_current_image_metadata(wim)->modified = true;
+ if (!(flags & (WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_NONE |
+ WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR |
+ WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_WINDOWS)))
+ flags |= WIMLIB_MOUNT_FLAG_STREAM_INTERFACE_XATTR;
+
mount_dir = dir;
working_directory = getcwd(NULL, 0);
if (!working_directory) {
- ERROR("Could not determine current directory: %m\n");
+ ERROR_WITH_ERRNO("Could not determine current directory");
return WIMLIB_ERR_NOTDIR;
}
mount_dir = dir;
pid = fork();
if (pid == -1) {
- ERROR("Failed to fork(): %m\n");
+ ERROR_WITH_ERRNO("Failed to fork()");
return WIMLIB_ERR_FORK;
}
if (pid == 0) {
execlp("fusermount", "fusermount", "-u", dir, NULL);
- ERROR("Failed to execute `fusermount': %m\n");
+ ERROR_WITH_ERRNO("Failed to execute `fusermount'");
return WIMLIB_ERR_FUSERMOUNT;
}
ret = waitpid(pid, &status, 0);
if (ret == -1) {
- ERROR("Failed to wait for fusermount process to "
- "terminate: %m\n");
+ ERROR_WITH_ERRNO("Failed to wait for fusermount process to "
+ "terminate");
return WIMLIB_ERR_FUSERMOUNT;
}
if (status != 0) {
- ERROR("fusermount exited with status %d!\n", status);
+ ERROR("fusermount exited with status %d", status);
return WIMLIB_ERR_FUSERMOUNT;
}
msg[0] = (flags & WIMLIB_UNMOUNT_FLAG_COMMIT) ? 1 : 0;
msg[1] = (flags & WIMLIB_UNMOUNT_FLAG_CHECK_INTEGRITY) ? 1 : 0;
- DEBUG("Sending message: %s, %s\n",
+ DEBUG("Sending message: %s, %s",
(msg[0] == 0) ? "don't commit" : "commit",
(msg[1] == 0) ? "don't check" : "check");
ret = mq_send(unmount_to_daemon_mq, msg, 2, 1);
if (ret == -1) {
- ERROR("Failed to notify filesystem daemon whether "
- "we want to commit changes or not!\n");
+ ERROR("Failed to notify filesystem daemon whether we want to "
+ "commit changes or not");
close_message_queues();
return WIMLIB_ERR_MQUEUE;
}
mailbox[0] = 0;
DEBUG("Waiting for message telling us whether the unmount was "
- "successful or not.\n");
+ "successful or not.");
ret = mq_timedreceive(daemon_to_unmount_mq, mailbox, msgsize,
NULL, &timeout);
errno_save = errno;
close_message_queues();
if (ret == -1) {
if (errno_save == ETIMEDOUT) {
- ERROR("Timed out- probably the filesystem "
- "daemon crashed and the WIM was not "
- "written successfully.\n");
+ ERROR("Timed out- probably the filesystem daemon "
+ "crashed and the WIM was not written "
+ "successfully.");
return WIMLIB_ERR_TIMEOUT;
} else {
- ERROR("mq_receive(): %s\n",
- strerror(errno_save));
+ ERROR("mq_receive(): %s", strerror(errno_save));
return WIMLIB_ERR_MQUEUE;
}
}
- DEBUG("Received message: %s\n", (mailbox[0] == 0) ?
- "Unmount OK" : "Unmount Failed");
+ DEBUG("Received message: %s",
+ (mailbox[0] == 0) ? "Unmount OK" : "Unmount Failed");
if (mailbox[0] != 0)
- ERROR("Unmount failed\n");
+ ERROR("Unmount failed");
return mailbox[0];
}
static inline int mount_unsupported_error()
{
- ERROR("WIMLIB was compiled with --without-fuse, which "
- "disables support for mounting WIMs.\n");
+ ERROR("WIMLIB was compiled with --without-fuse, which disables support "
+ "for mounting WIMs.");
return WIMLIB_ERR_UNSUPPORTED;
}