summaryrefslogtreecommitdiff
path: root/tbdiff/tbdiff-apply.c
diff options
context:
space:
mode:
Diffstat (limited to 'tbdiff/tbdiff-apply.c')
-rw-r--r--tbdiff/tbdiff-apply.c740
1 files changed, 740 insertions, 0 deletions
diff --git a/tbdiff/tbdiff-apply.c b/tbdiff/tbdiff-apply.c
new file mode 100644
index 0000000..e281d73
--- /dev/null
+++ b/tbdiff/tbdiff-apply.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2011-2012 Codethink Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <time.h>
+
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include <attr/xattr.h>
+
+#include <tbdiff/tbdiff-common.h>
+#include <tbdiff/tbdiff-io.h>
+#include <tbdiff/tbdiff-private.h>
+#include <tbdiff/tbdiff-xattrs.h>
+
+char*
+tbd_apply_fread_string(FILE *stream)
+{
+ uint16_t dlen;
+ if(tbd_read_uint16_t(&dlen, stream) != 1)
+ return NULL;
+ char dname[dlen + 1];
+ if(fread(dname, 1, dlen, stream) != dlen)
+ return NULL;
+ dname[dlen] = '\0';
+
+ return strdup(dname);
+}
+
+/* reads a block of data into memory
+ * using the address in *data which is assumed to be able to contain *size
+ * if it needs more than *size bytes to store the data, *data is reallocated
+ * providing initial values of *data = NULL and *size = 0 will force it to
+ * allocate the required memory itself
+ * do not supply a statically or dynamically allocated buffer unless:
+ * - you can guarantee it is not smaller than the data
+ * - or realloc doesn't free old memory (though this will be a memory leak)
+ * - or your allocator does nothing when asked to free non-allocated memory
+ */
+int tbd_apply_fread_block(FILE *stream, void **data, size_t *size)
+{
+ {
+ size_t _size;
+ if (fread(&_size, 1, sizeof(_size), stream) != sizeof(_size) ) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ }
+ if (_size > *size) {
+ void *allocres = realloc(*data, _size);
+ if (allocres == NULL) {
+ return TBD_ERROR(TBD_ERROR_OUT_OF_MEMORY);
+ }
+ *data = allocres;
+ *size = _size;
+ }
+ }
+
+ if (fread(*data, 1, *size, stream) != *size) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ }
+ return TBD_ERROR_SUCCESS;
+}
+
+static int
+tbd_apply_identify(FILE *stream)
+{
+ uint8_t cmd;
+ if(fread(&cmd, 1, 1, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(cmd != TBD_CMD_IDENTIFY)
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ uint16_t nlen;
+ if(tbd_read_uint16_t(&nlen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(strlen(TB_DIFF_PROTOCOL_ID) != nlen)
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ char nstr[nlen];
+ if(fread(nstr, 1, nlen, stream) != nlen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(strncmp(nstr, TB_DIFF_PROTOCOL_ID, nlen) != 0)
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ return 0;
+}
+
+static int
+tbd_apply_cmd_dir_create(FILE *stream)
+{
+ uint16_t dlen;
+ if(tbd_read_uint16_t(&dlen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char dname[dlen + 1];
+ if(fread(dname, 1, dlen, stream) != dlen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ dname[dlen] = '\0';
+ fprintf(stderr, "cmd_dir_create %s\n", dname);
+ if(strchr(dname, '/') != NULL)
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+
+ time_t mtime;
+ if(tbd_read_time_t(&mtime, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ uid_t uid;
+ if(tbd_read_uid_t(&uid, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ gid_t gid;
+ if(tbd_read_gid_t(&gid, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ mode_t mode;
+ if(tbd_read_mode_t(&mode, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ if(mkdir(dname, (mode_t)mode) != 0)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CREATE_DIR);
+
+ // Apply metadata.
+ struct utimbuf timebuff = { time(NULL), mtime };
+ utime(dname, &timebuff); // Don't care if it succeeds right now.
+
+ chown(dname, (uid_t)uid, (gid_t)gid);
+ chmod (dname, mode);
+
+ return 0;
+}
+
+static int
+tbd_apply_cmd_dir_enter(FILE *stream,
+ uintptr_t *depth)
+{
+ uint16_t dlen;
+ if(tbd_read_uint16_t(&dlen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char dname[dlen + 1];
+ if(fread(dname, 1, dlen, stream) != dlen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ dname[dlen] = '\0';
+ fprintf(stderr, "cmd_dir_enter %s\n", dname);
+ if((strchr(dname, '/') != NULL) || (strcmp(dname, "..") == 0))
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ if(depth != NULL)
+ (*depth)++;
+
+ if(chdir(dname) != 0)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ return 0;
+}
+
+static int
+tbd_apply_cmd_dir_leave(FILE *stream,
+ uintptr_t *depth)
+{
+ int err = TBD_ERROR_SUCCESS;
+ struct utimbuf time;
+
+ if (tbd_read_time_t(&(time.modtime), stream) != 1) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ }
+ time.actime = time.modtime;/* not sure what the best atime to use is */
+
+ fprintf(stderr, "cmd_dir_leave\n");
+
+ /* test for leaving shallowest depth */
+ if ((depth != NULL) && (*depth < 1)) {
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ }
+
+ if (utime(".", &time) == -1) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ }
+
+ if (chdir("..") != 0) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ }
+
+ if (depth != NULL) {
+ (*depth)--;
+ }
+
+ return err;
+}
+
+static int
+tbd_apply_cmd_file_create(FILE *stream)
+{
+ uint16_t flen;
+ if(tbd_read_uint16_t(&flen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char fname[flen + 1];
+ if(fread(fname, 1, flen, stream) != flen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ fname[flen] = '\0';
+ if((strchr(fname, '/') != NULL) || (strcmp(fname, "..") == 0))
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+
+ time_t mtime;
+ uint32_t mode;
+ uid_t uid;
+ gid_t gid;
+ uint32_t fsize;
+
+ if(tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_uint32_t(&mode, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1 ||
+ tbd_read_uint32_t(&fsize, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ fprintf(stderr, "cmd_file_create %s:%"PRId32"\n", fname, fsize);
+
+ FILE *fp = fopen(fname, "rb");
+ if(fp != NULL) {
+ fclose(fp);
+ return TBD_ERROR(TBD_ERROR_FILE_ALREADY_EXISTS);
+ }
+
+ fp = fopen(fname, "wb");
+ if(fp == NULL)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_OPEN_FILE_FOR_WRITING);
+
+ uintptr_t block = 256;
+ uint8_t fbuff[block];
+ for(; fsize != 0; fsize -= block) {
+ if(fsize < block)
+ block = fsize;
+ if(fread(fbuff, 1, block, stream) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(fwrite(fbuff, 1, block, fp) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_WRITE_STREAM);
+ }
+ fclose(fp);
+
+ // Apply metadata.
+ struct utimbuf timebuff = { time(NULL), mtime };
+
+ // Don't care if it succeeds right now.
+ utime(fname, &timebuff);
+ /* Chown ALWAYS have to be done before chmod */
+ chown(fname, (uid_t)uid, (gid_t)gid);
+ chmod(fname, mode);
+
+ return 0;
+}
+
+static int
+tbd_apply_cmd_file_delta(FILE *stream)
+{
+ uint16_t mdata_mask;
+ time_t mtime;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ uint16_t flen;
+ if(tbd_read_uint16_t(&flen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char fname[flen + 1];
+ if(fread(fname, 1, flen, stream) != flen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ fname[flen] = '\0';
+
+ fprintf(stderr, "cmd_file_delta %s\n", fname);
+
+ if((strchr(fname, '/') != NULL) ||
+ (strcmp(fname, "..") == 0))
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+
+ /* Reading metadata */
+ if(tbd_read_uint16_t(&mdata_mask, stream) != 1 ||
+ tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1 ||
+ tbd_read_uint32_t(&mode, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ FILE *op = fopen(fname, "rb");
+ if(op == NULL)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_OPEN_FILE_FOR_READING);
+ if(remove(fname) != 0) {
+ fclose(op);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_REMOVE_FILE);
+ }
+ FILE *np = fopen(fname, "wb");
+ if(np == NULL) {
+ fclose(op);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_OPEN_FILE_FOR_WRITING);
+ }
+
+ uint32_t dstart, dend;
+ if(tbd_read_uint32_t(&dstart, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(tbd_read_uint32_t(&dend, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ uintptr_t block;
+ uint8_t fbuff[256];
+ for(block = 256; dstart != 0; dstart -= block) {
+ if(dstart < block)
+ block = dstart;
+ if(fread(fbuff, 1, block, op) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(fwrite(fbuff, 1, block, np) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_WRITE_STREAM);
+ }
+
+ uint32_t fsize;
+ if(tbd_read_uint32_t(&fsize, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ for(block = 256; fsize != 0; fsize -= block) {
+ if(fsize < block)
+ block = fsize;
+ if(fread(fbuff, 1, block, stream) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ if(fwrite(fbuff, 1, block, np) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_WRITE_STREAM);
+ }
+
+ if(fseek(op, dend, SEEK_SET) != 0) {
+ fclose(np);
+ fclose(op);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_SEEK_THROUGH_STREAM);
+ }
+
+ for(block = 256; block != 0;) {
+ block = fread(fbuff, 1, block, op);
+ if(fwrite(fbuff, 1, block, np) != block)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_WRITE_STREAM);
+ }
+
+ fclose(np);
+ fclose(op);
+
+ // Apply metadata.
+ /* file was removed so old permissions were lost
+ * all permissions need to be reapplied, all were sent in this protocol
+ * if only changed sent will have to save mdata from file before it is
+ * removed, then change that data based on the mask
+ * it will still all have to be reapplied
+ */
+ {
+ struct utimbuf timebuff;
+ timebuff.modtime = mtime;
+ if (time(&(timebuff.actime)) == (time_t)-1) {
+ return TBD_ERROR(TBD_ERROR_FAILURE);
+ }
+ if (utime(fname, &timebuff) == -1) {
+ return TBD_ERROR(TBD_ERROR_FAILURE);
+ }
+ if (chown(fname, (uid_t)uid, (gid_t)gid) == -1) {
+ return TBD_ERROR(TBD_ERROR_FAILURE);
+ }
+ if (chmod(fname, mode) == -1) {
+ return TBD_ERROR(TBD_ERROR_FAILURE);
+ }
+ }
+
+ return 0;
+}
+
+static int tbd_apply_cmd_entity_delete_for_name(const char*);
+static int tbd_apply_cmd_dir_delete(const char *name)
+{
+ int err = TBD_ERROR_SUCCESS;
+ DIR *dp;
+ struct dirent *entry;
+ if ((dp = opendir(name)) == NULL) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_REMOVE_FILE);
+ }
+
+ if (chdir(name) != 0) {
+ closedir(dp);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ }
+
+ while ((entry = readdir(dp)) != NULL) {
+ if ((strcmp(entry->d_name, ".") == 0) ||
+ (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ if ((err = tbd_apply_cmd_entity_delete_for_name(entry->d_name))
+ != TBD_ERROR_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ if (chdir("..") != 0) {
+ err = TBD_ERROR(TBD_ERROR_UNABLE_TO_CHANGE_DIR);
+ goto cleanup;
+ }
+ if (rmdir(name) != 0) {
+ err = TBD_ERROR(TBD_ERROR_UNABLE_TO_REMOVE_FILE);
+ }
+cleanup:
+ closedir(dp);
+ return err;
+}
+
+static int
+tbd_apply_cmd_entity_delete_for_name(const char *name)
+{
+ struct stat info;
+ if (lstat(name, &info) != 0) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_STAT_FILE);
+ }
+
+ if (S_ISDIR(info.st_mode)) {
+ return tbd_apply_cmd_dir_delete(name);
+ }
+
+ if (unlink(name) != 0) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_REMOVE_FILE);
+ }
+
+ return TBD_ERROR_SUCCESS;
+}
+
+static int
+tbd_apply_cmd_entity_delete(FILE *stream)
+{
+ uint16_t elen;
+ if(tbd_read_uint16_t(&elen, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char ename[elen + 1];
+ if(fread(ename, 1, elen, stream) != elen)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ ename[elen] = '\0';
+
+ fprintf(stderr, "cmd_entity_delete %s\n", ename);
+
+ if((strchr(ename, '/') != NULL) || (strcmp(ename, "..") == 0))
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ return tbd_apply_cmd_entity_delete_for_name(ename);
+}
+
+static int
+tbd_apply_cmd_symlink_create(FILE *stream)
+{
+ uint16_t len;
+ time_t mtime;
+ uid_t uid;
+ gid_t gid;
+
+ if(tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ /* Reading link file name */
+ if(tbd_read_uint16_t(&len, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ char linkname[len + 1];
+ linkname[len] = '\0';
+ if(fread(linkname, sizeof(char), len, stream) != len)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ /* Reading target path */
+ if(tbd_read_uint16_t(&len, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ char linkpath[len+1];
+ linkpath[len] = '\0';
+
+ if(fread(linkpath, sizeof(char), len, stream) != len)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ fprintf(stderr, "cmd_symlink_create %s -> %s\n", linkname, linkpath);
+
+ if(symlink(linkpath, linkname))
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CREATE_SYMLINK);
+
+ struct timeval tv[2];
+ gettimeofday(&tv[0], NULL);
+ tv[1].tv_sec = (long) mtime;
+ tv[1].tv_usec = 0;
+
+ lutimes(linkname, tv); // Don't care if it succeeds right now.
+ lchown(linkname, (uid_t)uid, (uid_t)gid);
+
+ return TBD_ERROR_SUCCESS;
+}
+
+static int
+tbd_apply_cmd_special_create(FILE *stream)
+{
+ char *name = tbd_apply_fread_string(stream);
+ time_t mtime;
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ uint32_t dev;
+
+ if(name == NULL ||
+ tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_mode_t(&mode, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1 ||
+ tbd_read_uint32_t(&dev, stream) != 1) {
+ free(name);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ }
+
+ fprintf(stderr, "cmd_special_create %s\n", name);
+
+ if(mknod(name, mode, (dev_t)dev) != 0) {
+ free(name);
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_CREATE_SPECIAL_FILE);
+ }
+
+ struct utimbuf timebuff = { time(NULL), mtime };
+ utime(name, &timebuff); // Don't care if it succeeds right now.
+
+ chown(name, (uid_t)uid, (gid_t)gid);
+ chmod(name, mode);
+
+ free(name);
+ return TBD_ERROR_SUCCESS;
+}
+
+static int
+tbd_apply_cmd_dir_delta(FILE *stream)
+{
+ uint16_t metadata_mask;
+ time_t mtime;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+
+ if(tbd_read_uint16_t(&metadata_mask, stream) != 1 ||
+ tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1 ||
+ tbd_read_uint32_t(&mode, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ char *dname = tbd_apply_fread_string(stream);
+ if(dname == NULL)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ fprintf(stderr, "cmd_dir_delta %s\n", dname);
+
+ if(metadata_mask & TBD_METADATA_MTIME) {
+ struct utimbuf timebuff = { time(NULL), mtime };
+ utime(dname, &timebuff); // Don't care if it succeeds right now.
+ }
+ if(metadata_mask & TBD_METADATA_UID || metadata_mask & TBD_METADATA_GID)
+ chown(dname, (uid_t)uid, (gid_t)gid);
+ if(metadata_mask | TBD_METADATA_MODE)
+ chmod(dname, mode);
+
+ free(dname);
+ return TBD_ERROR_SUCCESS;
+}
+
+static int
+tbd_apply_cmd_file_mdata_update(FILE *stream)
+{
+ uint16_t metadata_mask;
+ time_t mtime;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+
+ if(tbd_read_uint16_t(&metadata_mask, stream) != 1 ||
+ tbd_read_time_t(&mtime, stream) != 1 ||
+ tbd_read_uid_t(&uid, stream) != 1 ||
+ tbd_read_gid_t(&gid, stream) != 1 ||
+ tbd_read_uint32_t(&mode, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ char *dname = tbd_apply_fread_string(stream);
+ if(dname == NULL)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+
+ fprintf(stderr, "cmd_metadata_update %s\n", dname);
+
+ if(metadata_mask & TBD_METADATA_MTIME) {
+ struct utimbuf timebuff = { time(NULL), mtime };
+ utime(dname, &timebuff); // Don't care if it succeeds right now.
+ }
+ if(metadata_mask & TBD_METADATA_UID || metadata_mask & TBD_METADATA_GID)
+ chown(dname, (uid_t)uid, (gid_t)gid);
+ if(metadata_mask | TBD_METADATA_MODE)
+ chmod(dname, mode);
+
+ free(dname);
+ return TBD_ERROR_SUCCESS;
+}
+
+static int tbd_apply_cmd_xattrs_update(FILE *stream)
+{
+ int err = TBD_ERROR_SUCCESS;
+ char *fname;
+ uint32_t count;
+ void *data = NULL;
+ size_t dsize = 0;
+ /* read the name of the file to operate on */
+ if ((fname = tbd_apply_fread_string(stream)) == NULL) {
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ }
+
+ /* remove all attributes in preparation for adding new ones */
+ if ((err = tbd_xattrs_removeall(fname)) != TBD_ERROR_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* read how many attributes to process */
+ if (tbd_read_uint32_t(&count, stream) != 1) {
+ err = TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ goto cleanup;
+ }
+
+ /* operate on each attribute */
+ while (count > 0) {
+ char *aname = tbd_apply_fread_string(stream);
+ if (aname == NULL) {
+ err=TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ goto cleanup;
+ }
+
+ /* read a block of data, reallocating if needed */
+ if ((err = tbd_apply_fread_block(stream, &data, &dsize))
+ != TBD_ERROR_SUCCESS) {
+ free(aname);
+ goto cleanup;
+ }
+
+ if (lsetxattr(fname, aname, data, dsize, 0) == -1) {
+ free(aname);
+ goto cleanup;
+ }
+
+ count--;
+ free(aname);
+ }
+cleanup:
+ free(data);
+ free(fname);
+ return err;
+}
+
+int
+tbd_apply(FILE *stream)
+{
+ if(stream == NULL)
+ return TBD_ERROR(TBD_ERROR_NULL_POINTER);
+
+ int err;
+ if((err = tbd_apply_identify(stream)) != 0)
+ return err;
+
+ uintptr_t depth = 0;
+ bool flush = false;
+ while(!flush) {
+ uint8_t cmd;
+ if(fread(&cmd, 1, 1, stream) != 1)
+ return TBD_ERROR(TBD_ERROR_UNABLE_TO_READ_STREAM);
+ switch(cmd) {
+ case TBD_CMD_DIR_CREATE:
+ if((err = tbd_apply_cmd_dir_create(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_DIR_ENTER:
+ if((err = tbd_apply_cmd_dir_enter(stream, &depth)) != 0)
+ return err;
+ break;
+ case TBD_CMD_DIR_LEAVE:
+ if((err = tbd_apply_cmd_dir_leave(stream, &depth)) != 0)
+ return err;
+ break;
+ case TBD_CMD_FILE_CREATE:
+ if((err = tbd_apply_cmd_file_create(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_FILE_DELTA:
+ if((err = tbd_apply_cmd_file_delta(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_SYMLINK_CREATE:
+ if((err = tbd_apply_cmd_symlink_create(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_SPECIAL_CREATE:
+ if((err = tbd_apply_cmd_special_create(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_DIR_DELTA:
+ if((err = tbd_apply_cmd_dir_delta(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_FILE_METADATA_UPDATE:
+ if((err = tbd_apply_cmd_file_mdata_update(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_XATTRS_UPDATE:
+ if ((err = tbd_apply_cmd_xattrs_update(stream)) !=
+ TBD_ERROR_SUCCESS) {
+ return err;
+ }
+ break;
+ case TBD_CMD_ENTITY_MOVE:
+ case TBD_CMD_ENTITY_COPY:
+ return TBD_ERROR(TBD_ERROR_FEATURE_NOT_IMPLEMENTED); // TODO - Implement.
+ case TBD_CMD_ENTITY_DELETE:
+ if((err = tbd_apply_cmd_entity_delete(stream)) != 0)
+ return err;
+ break;
+ case TBD_CMD_UPDATE:
+ flush = true;
+ break;
+ default:
+ fprintf(stderr, "Error: Invalid command 0x%02"PRIx8".\n", cmd);
+ return TBD_ERROR(TBD_ERROR_INVALID_PARAMETER);
+ }
+ }
+
+ return 0;
+}