static void fs_sis_replace_hash_file(struct sis_fs_file *file) { const char *hash_fname, *path = fs_file_path(&file->file); struct fs *super_fs = file->super->fs; string_t *temp_path; int ret; if (file->hash_input == NULL) { /* hash file didn't exist previously. we should be able to create it with link() */ if (fs_link(super_fs, path, file->hash_path) < 0) { if (errno == EEXIST) { /* the file was just created. it's probably a duplicate, but it's too much trouble trying to deduplicate it anymore */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } } return; } temp_path = t_str_new(256); hash_fname = strrchr(file->hash_path, '/'); if (hash_fname == NULL) hash_fname = file->hash_path; else { str_append_n(temp_path, file->hash_path, (hash_fname-file->hash_path) + 1); hash_fname++; } str_printfa(temp_path, "%s%s.tmp", super_fs->set.temp_file_prefix, hash_fname); /* replace existing hash file atomically */ ret = fs_link(super_fs, path, str_c(temp_path)); if (ret < 0 && errno == EEXIST) { /* either someone's racing us or it's a stale file. try to continue. */ if (fs_unlink(super_fs, str_c(temp_path)) < 0 && errno != ENOENT) i_error("fs-sis: %s", fs_last_error(super_fs)); ret = fs_link(super_fs, path, str_c(temp_path)); } if (ret < 0) { i_error("fs-sis: %s", fs_last_error(super_fs)); return; } if (fs_rename(super_fs, str_c(temp_path), file->hash_path) < 0) { if (errno == ENOENT) { /* apparently someone else just renamed it. ignore. */ } else { i_error("fs-sis: %s", fs_last_error(super_fs)); } (void)fs_unlink(super_fs, str_c(temp_path)); } }
static bool fs_sis_try_link(struct sis_fs_file *file) { const char *path = fs_file_path(&file->file); const struct stat *st; struct stat st2; st = i_stream_stat(file->hash_input, FALSE); /* we can use the existing file */ if (fs_link(file->super->fs, file->hash_path, path) < 0) { if (errno != ENOENT && errno != EMLINK) i_error("fs-sis: %s", fs_last_error(file->super->fs)); /* failed to use link(), continue as if it hadn't been equal */ return FALSE; } if (fs_stat(file->super->fs, path, &st2) < 0) { i_error("fs-sis: %s", fs_last_error(file->super->fs)); if (fs_unlink(file->super->fs, path) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } if (st->st_ino != st2.st_ino) { /* the hashes/ file was already replaced with something else */ if (fs_unlink(file->super->fs, path) < 0) i_error("fs-sis: %s", fs_last_error(file->super->fs)); return FALSE; } return TRUE; }
int do_link(const char *filename,uint64_t lv,uint32_t ts,char *ptr) { uint32_t inode,parent; uint8_t name[256]; EAT(ptr,filename,lv,'('); GETU32(inode,ptr); EAT(ptr,filename,lv,','); GETU32(parent,ptr); EAT(ptr,filename,lv,','); GETNAME(name,ptr,filename,lv,')'); EAT(ptr,filename,lv,')'); return fs_link(ts,inode,parent,strlen((char*)name),name); }
int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "%s: missing operand\n", argv[0]); return 1; } if (argc == 4) { if (fs_slink(argv[3], argv[2])) { fprintf(stderr, "%s: error: ", argv[0]); perror(NULL); } } else { if (fs_link(argv[2], argv[1])) { fprintf(stderr, "%s: error: ", argv[0]); perror(NULL); } } return 0; }
/* This is possibly the lengthiest function in the VFS (possibly in the entire kernel!). * It resolves a path until the last name in the path string, and then tries to create * a new file with that name. It requires a lot of in-sequence checks for permission, * existance, is-a-directory, and so forth. */ struct inode *fs_path_resolve_create_get(const char *path, int flags, mode_t mode, int *result, struct dirent **dirent) { // Step 1: Split the path up into directory to create in, and name of new file. int len = strlen(path) + 1; char tmp[len]; memcpy(tmp, path, len); char *del = strrchr(tmp, '/'); if(del) *del = 0; char *dirpath = del ? tmp : "."; char *name = del ? del + 1 : tmp; if(dirpath[0] == 0) dirpath = "/"; if(dirent) *dirent = 0; if(result) *result = 0; // Step 2: Resolve the target directory. struct inode *dir = fs_path_resolve_inode(dirpath, flags, result); if(!dir) return 0; if(!S_ISDIR(dir->mode)) { if(result) *result = -ENOTDIR; return 0; } // Step 3: Try to look up the file that we're trying to create. struct dirent *test = fs_dirent_lookup(dir, name, strlen(name)); if(test) { // If it was found, return it and its inode. if(dirent) *dirent = test; struct inode *ret = fs_dirent_readinode(test, true); if(ret) ret = fs_resolve_mount(ret); if(!ret) { if(dirent) *dirent = 0; if(result) *result = -EIO; vfs_dirent_release(test); } else { if(!dirent) vfs_dirent_release(test); if(result) *result = 0; } vfs_icache_put(dir); return ret; } // Didn't find the entry. Step 4: Create one. if(!vfs_inode_check_permissions(dir, MAY_WRITE, 0)) { if(result) *result = -EACCES; vfs_icache_put(dir); return 0; } if(dir->nlink == 1) { if(result) *result = -ENOSPC; vfs_icache_put(dir); return 0; } uint32_t id; // Step 4a: Allocate an inode. int r = fs_callback_fs_alloc_inode(dir->filesystem, &id); if(r) { if(result) *result = r; vfs_icache_put(dir); return 0; } // Step 4b: Read in that inode, and set some initial values (like creation time). struct inode *node = vfs_icache_get(dir->filesystem, id); if(!node) { vfs_icache_put(dir); if(result) *result = -EIO; return 0; } node->mode = mode; node->length = 0; node->ctime = node->mtime = time_get_epoch(); vfs_inode_set_dirty(node); // If we're making a directory, create the . and .. entries. if(S_ISDIR(mode)) { // Create . and .. if(fs_link(node, node, ".", 1, true)) r = -EPERM; if(fs_link(node, dir, "..", 2, true)) r = -EMLINK; } // Step 4c: Create the link for the directory entry to the inode. r = fs_link(dir, node, name, strlen(name), false); if(result) *result = !r ? 1 : r; if(dirent && !r) { *dirent = fs_dirent_lookup(dir, name, strlen(name)); if(!*dirent && node) { vfs_icache_put(node); vfs_icache_put(dir); if(result) *result = -EIO; return 0; } } vfs_icache_put(dir); return r ? 0 : node; }