// 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; }
// truncate a file to a given size // return zero on success // return negative on failure. int fskit_ftrunc( struct fskit_core* core, struct fskit_file_handle* fh, off_t new_size ) { fskit_file_handle_rlock( fh ); // sanity check if( (fh->flags & (O_RDWR | O_WRONLY)) == 0 ) { fskit_file_handle_unlock( fh ); return -EBADF; } int rc = fskit_run_user_trunc( core, fh->path, fh->fent, new_size, fh->app_data ); fskit_file_handle_unlock( fh ); return rc; }
// 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; }