// verify that an inode is still valid. // that is, there's a process with the given PID running, and it's an instance of the same program that created it. // to speed this up, only check the hash of the process binary if the modtime has changed // return 1 if valid // return 0 if not valid // return negative on error int runfs_inode_is_valid( struct runfs_inode* inode, pid_t pid ) { int rc = 0; struct pstat ps; int flags = 0; memset( &ps, 0, sizeof(struct pstat) ); if( inode->verify_discipline & RUNFS_VERIFY_HASH ) { flags |= PSTAT_HASH; } rc = pstat( pid, &ps, flags ); if( rc < 0 ) { fskit_error("pstat(%d) rc = %d\n", pid, rc ); return rc; } rc = runfs_inode_is_created_by_proc( inode, &ps, inode->verify_discipline ); if( rc < 0 ) { fskit_error("runfs_inode_is_created_by_proc(%d) rc = %d\n", pid, rc ); return rc; } return rc; }
// set up fskit for FUSE // this is the "easy" method that handles the filesystem initialization for you. // return 0 on success // return -ENOMEM on OOM // return -errno on error int fskit_fuse_init( struct fskit_fuse_state* state, void* core_state ) { int rc = 0; // set up library rc = fskit_library_init(); if( rc != 0 ) { fskit_error( "fskit_library_init rc = %d\n", rc ); return rc; } // set up fskit struct fskit_core* core = fskit_core_new(); if( core == NULL ) { return -ENOMEM; } rc = fskit_core_init( core, core_state ); if( rc != 0 ) { fskit_error( "fskit_core_init rc = %d\n", rc ); return rc; } return fskit_fuse_init_fs( state, core ); }
int main( int argc, char** argv ) { struct fskit_core* core = NULL; int rc; char name_buf[10]; struct fskit_file_handle* fh = NULL; void* output; rc = fskit_test_begin( &core, NULL ); if( rc != 0 ) { exit(1); } for( int i = 0; i < 256; i++ ) { sprintf(name_buf, "/%d", i ); fh = fskit_create( core, name_buf, 0, i, 0644, &rc ); if( fh == NULL ) { fskit_error("fskit_create('%s') rc = %d\n", name_buf, rc ); exit(1); } fskit_close( core, fh ); } for( int i = 0; i < 256; i++ ) { sprintf(name_buf, "/%d", i ); struct stat sb; memset( &sb, 0xFF, sizeof(struct stat) ); rc = fskit_stat( core, name_buf, 0, i, &sb ); if( rc != 0 ) { fskit_error("fskit_stat('%s') rc = %d\n", name_buf, rc ); exit(1); } fskit_debug("%s: stat(st_dev=%lX st_ino=%lX st_mode=%o st_nlink=%lu st_uid=%d st_gid=%d st_rdev=%lX st_size=%jd st_blksize=%ld st_blocks=%ld)\n", name_buf, sb.st_dev, sb.st_ino, sb.st_mode, sb.st_nlink, sb.st_uid, sb.st_gid, sb.st_rdev, sb.st_size, sb.st_blksize, sb.st_blocks ); } fskit_print_tree( stdout, fskit_core_get_root( core ) ); fskit_test_end( core, &output ); return 0; }
int main( int argc, char** argv ) { struct fskit_core core; int rc; char name_buf[10]; struct fskit_file_handle* fh = NULL; void* output; const struct timeval times[2] = { {12345, 67890}, {23456, 78901} }; rc = fskit_test_begin( &core, NULL ); if( rc != 0 ) { exit(1); } for( int i = 0; i < 256; i++ ) { sprintf(name_buf, "/%d", i ); fh = fskit_create( &core, name_buf, 0, i, 0644, &rc ); if( fh == NULL ) { fskit_error("fskit_create('%s') rc = %d\n", name_buf, rc ); exit(1); } fskit_close( &core, fh ); } for( int i = 0; i < 256; i++ ) { sprintf(name_buf, "/%d", i ); rc = fskit_utimes( &core, name_buf, 0, i, times ); if( rc != 0 ) { fskit_error("fskit_utimes('%s') rc = %d\n", name_buf, rc ); exit(1); } } fskit_print_tree( stdout, &core.root ); fskit_test_end( &core, &output ); return 0; }
// NOTE: fent cannot be locked int fskit_do_user_stat( struct fskit_core* core, char const* fs_path, struct fskit_entry* fent, struct stat* sb ) { int rc = 0; int cbrc = 0; char name[FSKIT_FILESYSTEM_NAMEMAX+1]; struct fskit_route_dispatch_args dargs; memset( name, 0, FSKIT_FILESYSTEM_NAMEMAX+1 ); fskit_basename( fs_path, name ); fskit_route_stat_args( &dargs, name, sb ); rc = fskit_route_call_stat( core, fs_path, fent, &dargs, &cbrc ); if( rc == -EPERM || rc == -ENOSYS ) { // no stat defined return 0; } else if( rc != 0 || cbrc != 0 ) { fskit_error("fskit_route_call_stat rc = %d, cbrc = %d\n", rc, cbrc ); } return cbrc; }
// read the target in a symlink (including the null character at the end) // return the number of bytes read on success (up to MIN(buflen, linkpath size)) // return -EINVAL if the entry isn't a symlink // return -EIO if the entry is corrupt (indicates a bug in fskit) // return -errno if path resolution fails ssize_t fskit_readlink( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, char* buf, size_t buflen ) { int err = 0; ssize_t num_read = 0; struct fskit_entry* fent = NULL; // find this fent = fskit_entry_resolve_path( core, path, user, group, false, &err ); if( err != 0 || fent == NULL ) { return err; } // symlink? if( fent->type != FSKIT_ENTRY_TYPE_LNK ) { fskit_entry_unlock( fent ); return -EINVAL; } // sanity check if( fent->symlink_target == NULL ) { fskit_error("BUG: fskit entry %" PRIX64 " (at %p) is a symlink, but has no target path set\n", fent->file_id, fent ); fskit_entry_unlock( fent ); return -EIO; } // read it (including null character) num_read = (ssize_t)MIN( buflen, (unsigned)fent->size + 1 ); memcpy( buf, fent->symlink_target, num_read ); fskit_entry_unlock( fent ); return num_read; }
// do a file open // child must *not* be locked. // parent must be write-locked, but it will be unlocked and re-locked. The child will be referenced during that time, so the parent is guaranteed not to disappear // on success, fill in the handle data int fskit_do_open( struct fskit_core* core, char const* path, struct fskit_entry* parent, struct fskit_entry* child, int flags, uint64_t user, uint64_t group, void** handle_data ) { int rc = 0; // block other processes so we can do access checks fskit_entry_wlock( child ); // sanity check if( child->link_count == 0 || child->deletion_in_progress || child->type == FSKIT_ENTRY_TYPE_DEAD ) { rc = -ENOENT; } // sanity check else if( child->type == FSKIT_ENTRY_TYPE_DIR ) { rc = -EISDIR; } // access control for normal open // check read/write status of flags, and bail on error else if( (!(flags & O_RDWR) && !(flags & O_WRONLY)) && !FSKIT_ENTRY_IS_READABLE(child->mode, child->owner, child->group, user, group) ) { rc = -EACCES; // not readable } else if( (flags & O_WRONLY) && !FSKIT_ENTRY_IS_WRITEABLE(child->mode, child->owner, child->group, user, group) ) { rc = -EACCES; // not writable } else if( (flags & O_RDWR) && (!FSKIT_ENTRY_IS_READABLE(child->mode, child->owner, child->group, user, group) || !FSKIT_ENTRY_IS_WRITEABLE(child->mode, child->owner, child->group, user, group)) ) { rc = -EACCES; // not readable or not writable } if( rc == 0 ) { // reference the child fskit_entry_ref_entry( child ); } fskit_entry_unlock( child ); if( rc != 0 ) { // can't open return rc; } // safe to allow access to the child while the user route is running, since the child (and thus the parent) can't get unlinked fskit_entry_unlock( parent ); // open will succeed according to fskit. invoke the user callback to generate handle data rc = fskit_run_user_open( core, path, child, flags, handle_data ); // reaquire... fskit_entry_wlock( parent ); if( rc != 0 ) { fskit_error("fskit_run_user_open(%s) rc = %d\n", path, rc ); fskit_entry_unref( core, path, child ); } return rc; }
// begin a functional test int fskit_test_begin( struct fskit_core* core, void* test_data ) { int rc = 0; rc = fskit_library_init(); if( rc != 0 ) { fskit_error("fskit_library_init rc = %d\n", rc ); return rc; } rc = fskit_core_init( core, test_data ); if( rc != 0 ) { fskit_error("fskit_core_init rc = %d\n", rc ); } return rc; }
int fskit_test_mkdir_LR_recursive( struct fskit_core* core, char const* path, int depth ) { if( depth <= 0 ) { return 0; } fskit_debug("mkdir('%s')\n", path ); int rc = fskit_mkdir( core, path, 0755, 0, 0 ); if( rc != 0 ) { fskit_error("fskit_mkdir('%s') rc = %d\n", path, rc ); return rc; } char* new_path_1 = fskit_fullpath( path, "L", NULL ); char* new_path_2 = fskit_fullpath( path, "R", NULL ); rc = fskit_test_mkdir_LR_recursive( core, new_path_1, depth - 1 ); if( rc != 0 ) { fskit_error("fskit_test_mkdir_LR_recursive('%s') rc = %d\n", new_path_1, rc ); free( new_path_1 ); free( new_path_2 ); return rc; } rc = fskit_test_mkdir_LR_recursive( core, new_path_2, depth - 1 ); if( rc != 0 ) { fskit_error("fskit_test_mkdir_LR_recursive('%s') rc = %d\n", new_path_2, rc ); free( new_path_1 ); free( new_path_2 ); return rc; } free( new_path_1 ); free( new_path_2 ); return 0; }
// end a functional test int fskit_test_end( struct fskit_core* core, void** test_data ) { int rc = 0; rc = fskit_detach_all( core, "/", core->root.children ); if( rc != 0 ) { fskit_error("fskit_detach_all(\"/\") rc = %d\n", rc ); return rc; } rc = fskit_core_destroy( core, test_data ); if( rc != 0 ) { fskit_error("fskit_core_destroy rc = %d\n", rc ); return rc; } rc = fskit_library_shutdown(); if( rc != 0 ) { return rc; } return rc; }
// shut down fskit fuse int fskit_fuse_shutdown( struct fskit_fuse_state* state, void** core_state ) { struct fskit_core* core = state->core; int rc = 0; if( core != NULL ) { // clean up // blow away all inodes rc = fskit_detach_all( core, "/" ); if( rc != 0 ) { fskit_error( "fskit_detach_all(\"/\") rc = %d\n", rc ); } // destroy the core rc = fskit_core_destroy( core, core_state ); if( rc != 0 ) { fskit_error( "fskit_core_destroy rc = %d\n", rc ); } // shut down the library rc = fskit_library_shutdown(); if( rc != 0 ) { fskit_error( "fskit_library_shutdown rc = %d\n", rc ); } free( core ); state->core = NULL; } // free mountpoint if( state->mountpoint != NULL ) { free( state->mountpoint ); state->mountpoint = NULL; } return rc; }
// truncate a file to a given size // return 0 on success // return negative on failure int fskit_trunc( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, off_t new_size ) { int err = 0; int rc = 0; // get the fent struct fskit_entry* fent = fskit_entry_resolve_path( core, path, user, group, true, &err ); if( fent == NULL || err != 0 ) { return err; } // reference the fent, so it won't go anywhere fent->open_count++; fskit_entry_unlock( fent ); rc = fskit_run_user_trunc( core, path, fent, new_size, NULL ); // unreference fskit_entry_wlock( fent ); fent->open_count--; // need to free? note that this may unlock and re-lock fent, but only if it cannot be resolved by any path // NOTE: this may unlock and destroy the fent rc = fskit_entry_try_destroy_and_free( core, path, fent ); if( rc > 0 ) { // fent was unlocked and destroyed rc = 0; } else if( rc < 0 ) { // some error occurred fskit_error("fskit_entry_try_destroy(%p) rc = %d\n", fent, rc ); fskit_entry_unlock( fent ); return rc; } else { // done with this entry fskit_entry_unlock( fent ); } return rc; }
int fskit_do_user_stat( struct fskit_core* core, char const* fs_path, struct fskit_entry* fent, struct stat* sb ) { int rc = 0; int cbrc = 0; struct fskit_route_dispatch_args dargs; fskit_route_stat_args( &dargs, sb ); rc = fskit_route_call_stat( core, fs_path, fent, &dargs, &cbrc ); if( rc == -EPERM || rc == -ENOSYS ) { // no stat defined return 0; } else if( rc != 0 || cbrc != 0 ) { fskit_error("fskit_route_call_stat rc = %d, cbrc = %d\n", rc, cbrc ); } return cbrc; }
int main( int argc, char** argv ) { struct fskit_core core; int rc; void* output; struct fskit_path_iterator* itr = NULL; struct fskit_file_handle* fh = NULL; rc = fskit_test_begin( &core, NULL ); if( rc != 0 ) { exit(1); } rc = fskit_test_mkdir_LR_recursive( &core, "/root", 7 ); if( rc != 0 ) { fskit_error("fskit_test_mkdir_LR_recursive('/root') rc = %d\n", rc ); exit(1); } fh = fskit_create( &core, "/root/L/R/L/R/L/R/.foo", 0, 0, 0777, &rc ); if( fh == NULL ) { fskit_error("fskit_create('/root/L/R/L/R/L/R/.foo') rc = %d\n", rc ); exit(1); } fskit_close( &core, fh ); fh = fskit_create( &core, "/bar.f", 0, 0, 0777, &rc ); if( fh == NULL ) { fskit_error("fskit_create('/bar.f') rc = %d\n", rc ); exit(1); } fskit_close( &core, fh ); rc = fskit_mkdir( &core, "/bar.d", 0755, 0, 0 ); if( rc < 0 ) { fskit_error("fskit_mkdir('/bar.d') rc = %d\n", rc ); exit(1); } ///////////////////////////////////////////////////////////////////////////////////// printf("\n\nIterate succeeds...\n\n"); for( itr = fskit_path_begin( &core, "/root/L/R/L/R/L/R/.foo", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); printf("\n\n"); for( itr = fskit_path_begin( &core, "/bar.f", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); printf("\n\n"); for( itr = fskit_path_begin( &core, "/bar.d", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); ///////////////////////////////////////////////////////////////////////////////////// printf("\n\nIterate succeeds on path with duplicate . and /...\n\n"); for( itr = fskit_path_begin( &core, "././root/L/R//L//././/R/L//.///R", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); printf("\n\n"); for( itr = fskit_path_begin( &core, "/././root///././/.//.", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); ///////////////////////////////////////////////////////////////////////////////////// printf("\n\nIterate fails (path too long)...\n\n"); for( itr = fskit_path_begin( &core, "/root/L/R/L/R/L/R/L/R/L/R/L/R/L/R/L/R", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); ///////////////////////////////////////////////////////////////////////////////////// printf("\n\nIterate fails (path does not exist)...\n\n"); for( itr = fskit_path_begin( &core, "/root/L/R/L/foo/L/R", true ); !fskit_path_end( itr ); fskit_path_next( itr ) ) { struct fskit_entry* cur = fskit_path_iterator_entry( itr ); char* cur_path = fskit_path_iterator_path( itr ); printf("Entry %016" PRIX64 " (%p): %c %s\n", fskit_entry_get_file_id( cur ), cur, fskit_entry_get_type( cur ) == FSKIT_ENTRY_TYPE_FILE ? 'F' : 'D', cur_path ); free( cur_path ); } printf("Iterator error: %d\n\n\n", fskit_path_iterator_error( itr ) ); fskit_path_iterator_release( itr ); fskit_test_end( &core, &output ); return 0; }
// print out a tree to the given file stream int fskit_print_tree( FILE* out, struct fskit_entry* root ) { struct fskit_entry* node = NULL; char* next_path = NULL; char type_str[10]; int rc = 0; fskit_entry_set_itr itr; fskit_entry_set* child = NULL; vector< struct fskit_entry* > frontier; vector< char* > frontier_paths; frontier.push_back( root ); frontier_paths.push_back( strdup(root->name) ); while( frontier.size() > 0 ) { node = frontier[0]; next_path = frontier_paths[0]; frontier.erase( frontier.begin() ); frontier_paths.erase( frontier_paths.begin() ); fskit_type_to_string( node->type, type_str ); fprintf( out, "%s: inode=%" PRIX64 " size=%jd mode=%o user=%" PRIu64 " group=%" PRIu64 " ctime=(%" PRId64 ".%" PRId32 ") mtime=(%" PRId64 ".%" PRId32 ") atime=(%" PRId64 ".%" PRId32 ") mem=%p \"%s\"\n", type_str, node->file_id, node->size, node->mode, node->owner, node->group, node->ctime_sec, node->ctime_nsec, node->mtime_sec, node->mtime_nsec, node->atime_sec, node->atime_nsec, node, next_path ); if( node->type == FSKIT_ENTRY_TYPE_DIR ) { if( node->children == NULL ) { fskit_error("ERR: children of %p == NULL\n", node ); rc = -EINVAL; break; } // explore children for( child = fskit_entry_set_begin( node->children, &itr ); child != NULL; child = fskit_entry_set_next( &itr ) ) { char const* name = fskit_entry_set_name_at( &itr ); struct fskit_entry* child = fskit_entry_set_child_at( &itr ); if( child == NULL ) { continue; } if( strcmp(".", name) == 0 || strcmp("..", name) == 0 ) { continue; } frontier.push_back( child ); frontier_paths.push_back( fskit_fullpath( next_path, child->name, NULL ) ); } } free( next_path ); } if( rc != 0 ) { for( unsigned int i = 0; i < frontier_paths.size(); i++ ) { if( frontier_paths.at(i) ) { free( frontier_paths.at(i) ); } } frontier_paths.clear(); } return rc; }
// close a directory handle, freeing it. // a directory may be unlinked on close, if this was the last handle to it, and its link count was zero. // if this happens, then app_dir_data will contain the directory's app data, and the directory will be freed. // return 0 on success // return the following errors: // * EBADF if the directory handle is invalid // * EDEADLK if there is a bug in the lock handling int fskit_closedir( struct fskit_core* core, struct fskit_dir_handle* dirh ) { int rc = 0; rc = fskit_dir_handle_wlock( dirh ); if( rc != 0 ) { // indicates deadlock; shouldn't happen fskit_error("BUG: fskit_dir_handle_wlock(%p) rc = %d\n", dirh, rc ); return rc; } if( dirh->dent == NULL ) { fskit_dir_handle_unlock( dirh ); return -EBADF; } // run user-given close route. Note that this may unlock dirh->dent and re-lock it, but only if it is fully unlinked. rc = fskit_run_user_close( core, dirh->path, dirh->dent, dirh->app_data ); if( rc != 0 ) { fskit_error("fskit_run_user_close(%s) rc = %d\n", dirh->path, rc ); fskit_dir_handle_unlock( dirh ); return rc; } rc = fskit_entry_wlock( dirh->dent ); if( rc != 0 ) { // shouldn't happen; indicates deadlock fskit_error("BUG: fskit_entry_wlock(%p) rc = %d\n", dirh->dent, rc ); fskit_dir_handle_unlock( dirh ); return rc; } // no longer open dirh->dent->open_count--; // see if we can destroy this.... // NOTE: this may unlock and free dirh->dent rc = fskit_entry_try_destroy_and_free( core, dirh->path, dirh->dent ); if( rc > 0 ) { // dent was unlocked and destroyed dirh->dent = NULL; rc = 0; } if( rc < 0 ) { // some error occurred fskit_error("fskit_entry_try_destroy(%p) rc = %d\n", dirh->dent, rc ); fskit_entry_unlock( dirh->dent ); return rc; } else { // not destroyed. // done with this directory fskit_entry_unlock( dirh->dent ); } // get rid of this handle fskit_dir_handle_unlock( dirh ); fskit_dir_handle_destroy( dirh ); return 0; }
// run fskit with fuse int fskit_fuse_main( struct fskit_fuse_state* state, int argc, char** argv ) { int rc = 0; // set up FUSE struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct fuse_chan* ch = NULL; struct fuse* fs = NULL; int multithreaded = 1; int foreground = 0; char* mountpoint = NULL; // parse command-line... rc = fuse_parse_cmdline( &args, &mountpoint, &multithreaded, &foreground ); if( rc < 0 ) { fskit_error("fuse_parse_cmdline rc = %d\n", rc ); fuse_opt_free_args(&args); return rc; } if( mountpoint == NULL ) { fskit_error("%s", "No mountpoint given\n"); fuse_opt_free_args(&args); return rc; } state->mountpoint = strdup( mountpoint ); // mount ch = fuse_mount( mountpoint, &args ); if( ch == NULL ) { rc = -errno; fskit_error("fuse_mount failed, errno = %d\n", rc ); fuse_opt_free_args(&args); if( rc == 0 ) { rc = -EPERM; } return rc; } // create the filesystem fs = fuse_new( ch, &args, &state->ops, sizeof(state->ops), state ); fuse_opt_free_args(&args); if( fs == NULL ) { // failed rc = -errno; fskit_error("fuse_new failed, errno = %d\n", rc ); fuse_unmount( mountpoint, ch ); if( rc == 0 ) { rc = -EPERM; } return rc; } // daemonize if running in the background fskit_debug("FUSE daemonize: foreground=%d\n", foreground); rc = fuse_daemonize( foreground ); if( rc != 0 ) { // failed fskit_error("fuse_daemonize(%d) rc = %d\n", foreground, rc ); fuse_unmount( mountpoint, ch ); fuse_destroy( fs ); return rc; } // set up FUSE signal handlers rc = fuse_set_signal_handlers( fuse_get_session(fs) ); if( rc < 0 ) { // failed fskit_error("fuse_set_signal_handlers rc = %d\n", rc ); fuse_unmount( mountpoint, ch ); fuse_destroy( fs ); return rc; } // if we have a post-mount callback, call it now, since FUSE is ready to receive requests if( state->postmount != NULL ) { rc = (*state->postmount)( state, state->postmount_cls ); if( rc != 0 ) { fskit_error("fskit postmount callback rc = %d\n", rc ); fuse_unmount( mountpoint, ch ); fuse_destroy( fs ); return rc; } } // run the filesystem--start processing requests fskit_debug("%s", "FUSE main loop entered\n"); if( multithreaded ) { rc = fuse_loop_mt( fs ); } else { rc = fuse_loop( fs ); } fskit_debug("%s", "FUSE main loop finished\n"); fuse_teardown( fs, mountpoint ); return rc; }
// open a directory. // On success, return an fskit_dir_handle // Return any error code via the *err argument (which will be non-zero if there was an error). // on error, return NULL, and set *err appropriately: // * -ENAMETOOLONG if _path is too long // * -EACCES if some part of _path is in accessible to the given user and group // * -ENOTDIR if the entry referred to by _path isn't a directory // * -ENOENT if the entry doesn't exist // * -ENOMEM on OOM struct fskit_dir_handle* fskit_opendir( struct fskit_core* core, char const* _path, uint64_t user, uint64_t group, int* err ) { void* app_handle_data = NULL; int rc = 0; struct fskit_entry* dir = NULL; struct fskit_dir_handle* dirh = NULL; char path[PATH_MAX]; if( strlen(_path) >= PATH_MAX ) { // too long *err = -ENAMETOOLONG; return NULL; } // ensure path ends in / memset( path, 0, PATH_MAX ); strncpy( path, _path, PATH_MAX - 1 ); fskit_sanitize_path( path ); dir = fskit_entry_resolve_path( core, path, user, group, true, err ); if( dir == NULL ) { // resolution error; err is set appropriately return NULL; } // make sure it's a directory if( dir->type != FSKIT_ENTRY_TYPE_DIR ) { *err = -ENOTDIR; fskit_entry_unlock( dir ); return NULL; } // reference it--it cannot be unlinked fskit_entry_ref_entry( dir ); fskit_entry_unlock( dir ); // generate handle data rc = fskit_run_user_open( core, path, dir, 0, &app_handle_data ); fskit_entry_wlock( dir ); if( rc != 0 ) { // user-run open code failed fskit_error("fskit_run_user_open(%s) rc = %d\n", path, rc ); fskit_entry_unlock( dir ); *err = rc; fskit_entry_unref( core, path, dir ); return NULL; } // make a handle to it dirh = fskit_dir_handle_create( dir, path, app_handle_data ); if( dirh == NULL ) { // OOM fskit_entry_unlock( dir ); fskit_entry_unref( core, path, dir ); *err = -ENOMEM; } else { // release the directory fskit_entry_unlock( dir ); } return dirh; }
// low-level mkdir: create a child and insert it into the parent. // parent must be a directory // parent must be write-locked // return -EEXIST if an entry with the given name (path_basename) already exists in parent. // return -EIO if we couldn't allocate an inode // return -ENOMEM if we couldn't allocate memory static int fskit_mkdir_lowlevel( struct fskit_core* core, char const* path, struct fskit_entry* parent, char const* path_basename, mode_t mode, uint64_t user, uint64_t group, void* cls ) { // resolve the child within the parent struct fskit_entry* child = fskit_entry_set_find_name( parent->children, path_basename ); int err = 0; void* app_dir_data = NULL; if( child != NULL ) { fskit_entry_wlock( child ); err = fskit_entry_try_garbage_collect( core, path, parent, child ); if( err >= 0 ) { if( err == 0 ) { // detached but not destroyed fskit_entry_unlock( child ); } // name is free child = NULL; } else { fskit_entry_unlock( child ); if( err == -EEXIST ) { // still exists; can't be garbage-collected return -EEXIST; } else { // shouldn't happen fskit_error("BUG: fskit_entry_try_garbage_collect(%s) rc = %d\n", path, err ); return -EIO; } } } if( child == NULL ) { // create an fskit_entry and attach it child = CALLOC_LIST( struct fskit_entry, 1 ); if( child == NULL ) { return -ENOMEM; } // get an inode for this child uint64_t child_inode = fskit_core_inode_alloc( core, parent, child ); if( child_inode == 0 ) { // error in allocation fskit_error("fskit_core_inode_alloc(%s) failed\n", path ); fskit_safe_free( child ); return -EIO; } // set up the directory err = fskit_entry_init_dir( child, parent, child_inode, user, group, mode ); if( err != 0 ) { fskit_error("fskit_entry_init_dir(%s) rc = %d\n", path, err ); fskit_safe_free( child ); return err; } // reference this directory, so it won't disappear during the user's route child->open_count++; // almost done. run the route callback for this path if needed err = fskit_run_user_mkdir( core, path, parent, child, mode, cls, &app_dir_data ); child->open_count--; if( err != 0 ) { // user route failed fskit_error("fskit_run_user_mkdir(%s) rc = %d\n", path, err ); fskit_entry_destroy( core, child, false ); fskit_safe_free( child ); } else { fskit_entry_set_user_data( child, app_dir_data ); // attach to parent fskit_entry_attach_lowlevel( parent, child, path_basename ); } }
// create/open a file, with the given flags and (if creating) mode // on success, return a file handle to the created/opened file. // on failure, return NULL and set *err to the appropriate errno struct fskit_file_handle* fskit_open_ex( struct fskit_core* core, char const* _path, uint64_t user, uint64_t group, int flags, mode_t mode, void* cls, int* err ) { if( fskit_check_flags( flags ) != 0 ) { *err = -EINVAL; return NULL; } int rc = 0; char* path = strdup(_path); if( path == NULL ) { *err = -ENOMEM; return NULL; } fskit_sanitize_path( path ); size_t basename_len = fskit_basename_len( path ); if( basename_len > FSKIT_FILESYSTEM_NAMEMAX ) { free( path ); *err = -ENAMETOOLONG; return NULL; } void* handle_data = NULL; // resolve the parent of this child (and write-lock it) char* path_dirname = fskit_dirname( path, NULL ); char path_basename[FSKIT_FILESYSTEM_NAMEMAX + 1]; memset( path_basename, 0, FSKIT_FILESYSTEM_NAMEMAX + 1 ); fskit_basename( path, path_basename ); struct fskit_file_handle* ret = NULL; // write-lock parent--we need to ensure that the child does not disappear on us between attaching it and routing the user-given callback struct fskit_entry* parent = fskit_entry_resolve_path( core, path_dirname, user, group, true, err ); if( parent == NULL ) { fskit_safe_free( path_dirname ); fskit_safe_free( path ); // err is set appropriately return NULL; } fskit_safe_free( path_dirname ); rc = fskit_do_parent_check( parent, flags, user, group ); if( rc != 0 ) { // can't perform this operation fskit_entry_unlock( parent ); fskit_safe_free( path ); *err = rc; return NULL; } // resolve the child (which may be in the process of being deleted) struct fskit_entry* child = fskit_entry_set_find_name( parent->children, path_basename ); bool created = false; if( flags & O_CREAT ) { if( child != NULL ) { fskit_entry_wlock( child ); // it might have been marked for garbage-collection rc = fskit_entry_try_garbage_collect( core, path, parent, child ); if( rc >= 0 ) { if( rc == 0 ) { // not destroyed, but no longer attached fskit_entry_unlock( child ); } // safe to create child = NULL; } else { // can't garbage-collect--child still exists fskit_entry_unlock( parent ); fskit_entry_unlock( child ); fskit_safe_free( path ); if( rc == -EEXIST ) { *err = -EEXIST; return NULL; } else { // shouldn't happen fskit_error("BUG: fskit_entry_try_garbage_collect(%s) rc = %d\n", path, rc ); *err = -EIO; } return NULL; } } if( child == NULL ) { // can create! // NOTE: do *not* lock child--it has to be unlocked for running user-given routes rc = fskit_do_create( core, parent, path, mode, user, group, cls, &child, &handle_data ); if( rc != 0 ) { fskit_entry_unlock( parent ); fskit_safe_free( path ); *err = rc; return NULL; } // created! created = true; } } else if( child == NULL ) { // not found fskit_entry_unlock( parent ); fskit_safe_free( path ); *err = -ENOENT; return NULL; } // now child exists. // don't lock it, though, since we have to pass it to truncate or open // do we have to truncate? if( (flags & O_TRUNC) && (flags & (O_RDWR | O_WRONLY)) ) { // run user truncate // NOTE: do *not* lock it--it has to be unlocked for running user-given routes rc = fskit_run_user_trunc( core, path, child, 0, NULL ); if( rc != 0 ) { // truncate failed fskit_entry_unlock( parent ); fskit_safe_free( path ); *err = rc; return NULL; } } if( !created ) { // do the open // NOTE: do *not* lock it--it has to be unlocked for running user-given routes rc = fskit_do_open( core, path, parent, child, flags, user, group, &handle_data ); if( rc != 0 ) { // open failed fskit_entry_unlock( parent ); fskit_safe_free( path ); *err = rc; return NULL; } } // done with parent fskit_entry_unlock( parent ); // still here--we can open the file now! fskit_entry_set_atime( child, NULL ); ret = fskit_file_handle_create( core, child, path, flags, handle_data ); fskit_safe_free( path ); if( ret == NULL ) { // only possible if we're out of memory! *err = -ENOMEM; } return ret; }
int main( int argc, char** argv ) { struct fskit_core core; int rc; char name_buf[10]; char name_buf2[10]; struct fskit_file_handle* fh = NULL; void* output; rc = fskit_test_begin( &core, NULL ); if( rc != 0 ) { exit(1); } // setup for( int i = 0; i < 10; i++ ) { // create /a$i memset(name_buf, 0, 10 ); sprintf(name_buf, "/a%d", i ); fh = fskit_create( &core, name_buf, 0, i, 0644, &rc ); if( fh == NULL ) { fskit_error("fskit_create('%s') rc = %d\n", name_buf, rc ); exit(1); } fskit_close( &core, fh ); // mkdir /d$i memset(name_buf, 0, 10 ); sprintf(name_buf, "/d%d", i ); rc = fskit_mkdir( &core, name_buf, 0755, 0, 0 ); if( rc != 0 ) { fskit_error("fskit_mkdir('%s') rc = %d\n", name_buf, rc ); exit(1); } } printf("Initial tree:\n"); fskit_print_tree( stdout, &core.root ); // rename in the same directory // rename /a$i to /b$i for( int i = 0; i < 10; i++ ) { memset(name_buf, 0, 10 ); memset(name_buf2, 0, 10 ); sprintf(name_buf, "/a%d", i ); sprintf(name_buf2, "/b%d", i ); rc = fskit_rename( &core, name_buf, name_buf2, 0, 0 ); if( rc != 0 ) { fskit_error("fskit_rename('%s', '%s') rc = %d\n", name_buf, name_buf2, rc ); exit(1); } } printf("Rename /a$i to /b$i"); fskit_print_tree( stdout, &core.root ); // rename into a deeper directory // rename /b$i to /d$i/a$i for( int i = 0; i < 10; i++ ) { memset(name_buf, 0, 10 ); memset(name_buf2, 0, 10 ); sprintf(name_buf, "/b%d", i ); sprintf(name_buf2, "/d%d/a%d", i, i ); rc = fskit_rename( &core, name_buf, name_buf2, 0, 0 ); if( rc != 0 ) { fskit_error("fskit_rename('%s', '%s') rc = %d\n", name_buf, name_buf2, rc ); exit(1); } } printf("Rename /b$i to /d$i/a$i"); fskit_print_tree( stdout, &core.root ); // rename into a shallower directory // rename /d$i/a$i to /a$i for( int i = 0; i < 10; i++ ) { memset(name_buf, 0, 10 ); memset(name_buf2, 0, 10 ); sprintf(name_buf, "/d%d/a%d", i, i ); sprintf(name_buf2, "/a%d", i ); rc = fskit_rename( &core, name_buf, name_buf2, 0, 0 ); if( rc != 0 ) { fskit_error("fskit_rename('%s', '%s') rc = %d\n", name_buf, name_buf2, rc ); exit(1); } } printf("Rename /d/a$i to /a$i"); fskit_print_tree( stdout, &core.root ); fskit_test_end( &core, &output ); return 0; }