// free a directory handle static void fskit_dir_handle_destroy( struct fskit_dir_handle* dirh ) { dirh->dent = NULL; if( dirh->path != NULL ) { fskit_safe_free( dirh->path ); dirh->path = NULL; } pthread_rwlock_destroy( &dirh->lock ); memset( dirh, 0, sizeof(struct fskit_dir_handle) ); fskit_safe_free( dirh ); }
// 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; }
// 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 ); } }