int build_xpath(char *xpath, const char *path, int rev) { struct file_record *fr; char sha1_digest[40]; if (!rev) { /* Search on FS */ strcpy(xpath, ROOTENV->fsback); strcat(xpath, path); return 0; } if (!(fr = find_fr(path, rev))) { PHOENIXFS_DBG("build_xpath:: missing: %s@%d", path, rev); /* Might be a directory; copy and give to caller */ strcpy(xpath, path); return -1; } print_sha1(sha1_digest, fr->sha1); sprintf(xpath, "%s/.git/loose/%s", ROOTENV->fsback, sha1_digest); if (access(xpath, F_OK) < 0) { /* Try extracting from packfile */ sprintf(xpath, "%s/.git/loose", ROOTENV->fsback); if (unpack_entry(fr->sha1, xpath) < 0) return -1; else PHOENIXFS_DBG("open:: pack %s", sha1_digest); } else PHOENIXFS_DBG("open:: loose %s", sha1_digest); return 0; }
void fstree_remove_file(const char *path) { struct dir_record *dr; struct vfile_record *vfr; uint16_t key = ~0; size_t length; char *filename; int rev; filename = split_basename(path, dirname); length = (size_t) strlen((char *) filename); key = compute_crc32(key, (const unsigned char *) filename, length); if (!(dr = find_dr(dirname))) { PHOENIXFS_DBG("fstree_remove_file:: missing %s", dirname); return; } if (!(vfr = find(dr->vroot, key, 0))) { PHOENIXFS_DBG("fstree_remove_file:: missing %s", filename); return; } for (rev = 0; ; rev++) { if (!vfr->history[rev]) break; free(vfr->history[rev]); } PHOENIXFS_DBG("fstree_remove_file:: %s", path); delete(dr->vroot, key); }
/* filename is simply a pointer; dirname must have alloc'ed memory */ char *split_basename(const char *path, char *dirname) { int length; char *filename; /* In the worst case, strrchr returns 0: leading '/' */ filename = strrchr(path, '/') + 1; /* Strip trailing '/' from all directories except '/' itself */ /* +1 to accomodate the null terminator */ length = strlen(path) - strlen(filename); length = (length == 1 ? 2 : length); if (!dirname) { PHOENIXFS_DBG("split_basename:: path: %s, filename: %s", path, filename); return filename; } memcpy(dirname, path, length - 1); dirname[length - 1] = '\0'; PHOENIXFS_DBG("split_basename:: path: %s, dirname: %s, filename: %s", path, dirname, filename); return filename; }
static int phoenixfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { DIR *dp; struct dirent *de; struct stat st; void *record; struct node *iter_root, *iter; struct vfile_record *vfr; struct dir_record *dr; register int i; dp = (DIR *) (uintptr_t) fi->fh; if (!(de = readdir(dp))) return -errno; /* Fill directories from backing FS */ do { /* Hide the .git directory, and enumerate only directories */ if (strcmp(de->d_name, ".git") && de->d_type == DT_DIR) { PHOENIXFS_DBG("readdir:: fs: %s", de->d_name); if (filler(buf, de->d_name, NULL, 0)) return -ENOMEM; } } while ((de = readdir(dp)) != NULL); /* Fill files from fstree */ if (!(dr = find_dr(path)) || !dr->vroot) { PHOENIXFS_DBG("readdir:: fstree: blank"); return 0; } iter_root = dr->vroot; iter = dr->vroot; /* Use only the leaves */ while (!iter->is_leaf) iter = iter->pointers[0]; while (1) { for (i = 0; i < iter->num_keys; i++) { if (!(record = find(iter_root, iter->keys[i], 0))) PHOENIXFS_DBG("readdir:: key listing issue"); vfr = (struct vfile_record *) record; fill_stat(&st, vfr->history[vfr->HEAD]); PHOENIXFS_DBG("readdir:: tree fill: %s", (const char *) vfr->name); if (filler(buf, (const char *) vfr->name, &st, 0)) return -ENOMEM; } if (iter->pointers && iter->pointers[BTREE_ORDER - 1] != NULL) iter = iter->pointers[BTREE_ORDER - 1]; else break; } return 0; }
struct dir_record *find_dr(const char *path) { struct dir_record *dr; uint16_t key = ~0; size_t length; length = (size_t) strlen((char *) path); key = compute_crc32(key, (const unsigned char *) path, length); if (!(dr = find(fsroot, key, 0))) { PHOENIXFS_DBG("find_dr:: missing %s", path); return NULL; } PHOENIXFS_DBG("find_dr:: found %s", path); return dr; }
static int phoenixfs_getattr(const char *path, struct stat *stbuf) { struct file_record *fr; struct dir_record *dr; int rev; rev = parse_pathspec(xpath, path); build_xpath(openpath, xpath, rev); PHOENIXFS_DBG("getattr:: %s %d", openpath, rev); /* Try underlying FS */ if (lstat(openpath, stbuf) < 0) { /* Try fstree */ if (!(dr = find_dr(xpath))) { if (!(fr = find_fr(xpath, rev))) return -ENOENT; else { memset(stbuf, 0, sizeof(struct stat)); fill_stat(stbuf, fr); return 0; } } memset(stbuf, 0, sizeof(struct stat)); stbuf->st_mode = S_IFDIR | 0755; } return 0; }
/** * Format: * <> := * num_keys | [key | name_len | name | [<load_vfr>][...]][...] */ struct node *load_dr_tree(FILE *infile) { struct node *root; struct dir_record *dr; uint16_t key; uint16_t num_keys; uint16_t name_len; register int i; root = NULL; memset(&num_keys, 0, sizeof(uint16_t)); fread(&num_keys, sizeof(uint16_t), 1, infile); for (i = 0; i < num_keys; i++) { memset(&key, 0, sizeof(uint16_t)); fread(&key, sizeof(uint16_t), 1, infile); memset(&name_len, 0, sizeof(uint16_t)); fread(&name_len, sizeof(uint16_t), 1, infile); memset(&path_buf, 0, PATH_MAX); fread(&path_buf, name_len * sizeof(unsigned char), 1, infile); PHOENIXFS_DBG("load_dr_tree:: %s", (const char *) path_buf); dr = make_dr((const char *) path_buf); root = insert(root, key, (void *) dr); dr->vroot = load_vfr_tree(infile); } return root; }
/** * Format: * <> := * num_keys | [key | name_len | name | [<dump_vfr>][...]][...] */ void dump_dr_tree(struct node *root, FILE *outfile) { struct dir_record *dr; uint16_t name_len; register int i; node *iter; iter = root; while (!iter->is_leaf) iter = iter->pointers[0]; fwrite(&(iter->num_keys), sizeof(uint16_t), 1, outfile); while (1) { for (i = 0; i < iter->num_keys; i++) { /* Write the key */ fwrite(&(iter->keys[i]), sizeof(uint16_t), 1, outfile); dr = find(root, iter->keys[i], 0); /* Compute name_len; write name_len and name */ name_len = strlen((const char *) dr->name); fwrite(&name_len, sizeof(uint16_t), 1, outfile); fwrite(dr->name, name_len * sizeof(unsigned char), 1, outfile); PHOENIXFS_DBG("dump_dr_tree:: %s", (const char *) dr->name); dump_vfr_tree(dr->vroot, outfile); } if (iter->pointers[BTREE_ORDER - 1] != NULL) iter = iter->pointers[BTREE_ORDER - 1]; else break; } }
/** * Format: * <> := * num_keys | [key | name_len | name | rev_nr | [<load_frs>][...]][...] */ struct node *load_vfr_tree(FILE *infile) { struct node *root; struct vfile_record *vfr; uint16_t key; uint16_t num_keys; uint16_t name_len; uint8_t rev_nr; register int i, j; root = NULL; memset(&num_keys, 0, sizeof(uint16_t)); fread(&num_keys, sizeof(uint16_t), 1, infile); for (i = 0; i < num_keys; i++) { memset(&key, 0, sizeof(uint16_t)); fread(&key, sizeof(uint16_t), 1, infile); memset(&name_len, 0, sizeof(uint16_t)); fread(&name_len, sizeof(uint16_t), 1, infile); memset(&path_buf, 0, PATH_MAX); fread(&path_buf, name_len * sizeof(unsigned char), 1, infile); memset(&rev_nr, 0, sizeof(uint8_t)); fread(&rev_nr, sizeof(uint8_t), 1, infile); vfr = make_vfr((const char *) path_buf); root = insert(root, key, (void *) vfr); for (j = 0; j < rev_nr; j++) { vfr->history[j] = malloc(sizeof(struct file_record)); memset(vfr->history[j], 0, sizeof(struct file_record)); fread(vfr->history[j], sizeof(struct file_record), 1, infile); PHOENIXFS_DBG("load_vfr_tree:: %s [%d]", vfr->name, j); } vfr->HEAD = rev_nr - 1; } return root; }
void fstree_insert_update_file(const char *path, const char *follow) { struct dir_record *dr; struct vfile_record *vfr; struct file_record *fr, *new_fr; uint16_t key = ~0; char *filename; size_t length; filename = split_basename(path, dirname); if (!(dr = find_dr(dirname))) goto DR; else { length = (size_t) strlen((char *) filename); key = compute_crc32(key, (const unsigned char *) filename, length); if (!(vfr = find(dr->vroot, key, 0))) { PHOENIXFS_DBG("fstree_insert_update_file:: missing vfr: %s", filename); goto VFR; } else { if (vfr->HEAD >= 0) fr = vfr->history[vfr->HEAD]; else fr = NULL; goto FR; } } DR: dr = make_dr(dirname); insert_dr(dr); VFR: vfr = make_vfr(filename); insert_vfr(dr, vfr); FR: if (!(new_fr = make_fr(path, follow))) { PHOENIXFS_DBG("fstree_insert_update_file:: Can't make fr %s", path); return; } /* If content is present in the old fr, don't make a new fr */ if (fr && !memcmp(fr->sha1, new_fr->sha1, 20)) { PHOENIXFS_DBG("fstree_insert_update_file:: unmodified: %s", path); free(new_fr); return; } insert_fr(vfr, new_fr); }
static int phoenixfs_chmod(const char *path, mode_t mode) { PHOENIXFS_DBG("chmod:: %s", path); build_xpath(xpath, path, 0); if (chmod(xpath, mode) < 0) return -errno; return 0; }
static void phoenixfs_destroy(void *userdata) { FILE *outfile; /* Persist the fstree */ sprintf(xpath, "%s/.git/fstree", ROOTENV->fsback); if (!(outfile = fopen(xpath, "wb"))) { PHOENIXFS_DBG("destroy:: Can't open .git/fstree to persist"); return; } PHOENIXFS_DBG("destroy:: dumping fstree"); fstree_dump_tree(outfile); fclose(outfile); PHOENIXFS_DBG("destroy:: packing loose objects"); sprintf(xpath, "%s/.git/loose", ROOTENV->fsback); dump_packing_info(xpath); }
static int phoenixfs_truncate(const char *path, off_t newsize) { PHOENIXFS_DBG("truncate:: %s to %lld", path, (long long int)newsize); build_xpath(xpath, path, 0); if (truncate(xpath, newsize) < 0) return -errno; return 0; }
static int phoenixfs_mkdir(const char *path, mode_t mode) { PHOENIXFS_DBG("mkdir:: %s", path); build_xpath(xpath, path, 0); if (mkdir(xpath, mode) < 0) return -errno; return 0; }
static int phoenixfs_statfs(const char *path, struct statvfs *statv) { PHOENIXFS_DBG("statfs:: %s", path); build_xpath(xpath, path, 0); if (statvfs(xpath, statv) < 0) return -errno; return 0; }
static int phoenixfs_mknod(const char *path, mode_t mode, dev_t dev) { PHOENIXFS_DBG("mknod:: %s", path); build_xpath(xpath, path, 0); if (mknod(xpath, mode, dev) < 0) return -errno; return 0; }
static int phoenixfs_access(const char *path, int mask) { PHOENIXFS_DBG("access:: %s", path); build_xpath(xpath, path, 0); if (access(xpath, mask) < 0) return -errno; return 0; }
static int phoenixfs_utime(const char *path, struct utimbuf *ubuf) { PHOENIXFS_DBG("utime:: %s", path); build_xpath(xpath, path, 0); if (utime(xpath, ubuf) < 0) return -errno; return 0; }
static int phoenixfs_fgetattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) { PHOENIXFS_DBG("fgetattr:: %s", path); if (fstat(fi->fh, stbuf) < 0) return -errno; return 0; }
struct file_record *find_fr(const char *path, int rev) { struct vfile_record *vfr; struct file_record *fr; if (!(vfr = find_vfr(path))) { PHOENIXFS_DBG("find_fr:: not found %s", path); return NULL; } /* Translate rev to mean "number of revs before HEAD" */ rev = (vfr->HEAD - rev) % REV_TRUNCATE; if (!(fr = vfr->history[rev])) { PHOENIXFS_DBG("find_fr:: not found %s", path); return NULL; } PHOENIXFS_DBG("find_fr:: found %s", path); return fr; }
static int phoenixfs_ftruncate(const char *path, off_t offset, struct fuse_file_info *fi) { PHOENIXFS_DBG("ftruncate:: %s", path); build_xpath(xpath, path, 0); if (ftruncate(fi->fh, offset) < 0) return -errno; return 0; }
static int phoenixfs_readlink(const char *path, char *link, size_t size) { /* Always pass through to underlying filesystem */ PHOENIXFS_DBG("readlink:: %s", path); build_xpath(xpath, path, 0); if (readlink(xpath, link, size - 1) < 0) return -errno; return 0; }
static int phoenixfs_rmdir(const char *path) { /* Always pass through to underlying filesystem */ PHOENIXFS_DBG("rmdir:: %s", path); build_xpath(xpath, path, 0); if (rmdir(xpath) < 0) return -errno; return 0; }
void insert_fr(struct vfile_record *vfr, struct file_record *fr) { int newHEAD; newHEAD = (vfr->HEAD + 1) % REV_TRUNCATE; vfr->history[newHEAD] = fr; vfr->HEAD = newHEAD; PHOENIXFS_DBG("insert_fr:: %s [%d]", vfr->name, vfr->HEAD); }
static int phoenixfs_open(const char *path, struct fuse_file_info *fi) { int rev, fd; FILE *infile, *fsfile; char fspath[PATH_MAX]; struct file_record *fr; char sha1_digest[40]; rev = parse_pathspec(xpath, path); build_xpath(fspath, xpath, 0); /* Skip zinflate for entries not in fstree */ if (!(fr = find_fr(xpath, rev))) goto END; /* Build openpath by hand */ print_sha1(sha1_digest, fr->sha1); sprintf(openpath, "%s/.git/loose/%s", ROOTENV->fsback, sha1_digest); if (access(openpath, F_OK) < 0) { /* Try extracting from packfile */ sprintf(xpath, "%s/.git/loose", ROOTENV->fsback); if (unpack_entry(fr->sha1, xpath) < 0) return -ENOENT; else PHOENIXFS_DBG("open:: pack %s", sha1_digest); } else PHOENIXFS_DBG("open:: loose %s", sha1_digest); /* zinflate openpath onto fspath */ PHOENIXFS_DBG("open:: zinflate %s onto %s", sha1_digest, fspath); if (!(infile = fopen(openpath, "rb")) || !(fsfile = fopen(fspath, "wb+"))) return -errno; if (zinflate(infile, fsfile) != Z_OK) PHOENIXFS_DBG("open:: zinflate issue"); fclose(infile); fclose(fsfile); END: if ((fd = open(fspath, fi->flags)) < 0) return -errno; fi->fh = fd; return 0; }
/** * Format: * <> := * (struct file_record) */ static void dump_frs(struct vfile_record *vfr, uint8_t start_rev, uint8_t rev_nr, FILE *outfile) { while (start_rev < rev_nr) { fwrite(vfr->history[start_rev], sizeof(struct file_record), 1, outfile); PHOENIXFS_DBG("dump_frs:: %s [%u]", vfr->name, start_rev); start_rev = (start_rev + 1) % REV_TRUNCATE; } }
static int phoenixfs_unlink(const char *path) { /* Always pass through to underlying filesystem */ PHOENIXFS_DBG("unlink:: %s", path); fstree_remove_file(path); build_xpath(xpath, path, 0); if (unlink(xpath) < 0) return -errno; return 0; }
void insert_vfr(struct dir_record *dr, struct vfile_record *vfr) { uint16_t key = ~0; size_t length; length = (size_t) strlen((char *) vfr->name); key = compute_crc32(key, (const unsigned char *) vfr->name, length); PHOENIXFS_DBG("insert_vfr:: %08X", key); dr->vroot = insert(dr->vroot, key, vfr); }
void insert_dr(struct dir_record *dr) { uint16_t key = ~0; size_t length; length = (size_t) strlen((char *) dr->name); key = compute_crc32(key, (const unsigned char *) dr->name, length); PHOENIXFS_DBG("insert_dr:: %08X", key); fsroot = insert(fsroot, key, dr); }
struct dir_record *make_dr(const char *path) { struct dir_record *dr; PHOENIXFS_DBG("make_dr:: %s", path); if (!(dr = malloc(sizeof(struct dir_record)))) return NULL; memcpy(dr->name, path, strlen(path) + 1); dr->vroot = NULL; return dr; }