// write up to buflen bytes into buf, starting at the given offset in the file. // return the number of bytes written on success. // return negative on failure. ssize_t fskit_write( struct fskit_core* core, struct fskit_file_handle* fh, char const* buf, size_t buflen, off_t offset ) { fskit_file_handle_rlock( fh ); // sanity check if( (fh->flags & (O_RDWR | O_WRONLY)) == 0 ) { fskit_file_handle_unlock( fh ); return -EBADF; } ssize_t num_written = fskit_run_user_write( core, fh->path, fh->fent, buf, buflen, offset, fh->app_data ); if( num_written >= 0 ) { // update metadata fskit_entry_wlock( fh->fent ); fskit_entry_set_mtime( fh->fent, NULL ); fskit_entry_set_atime( fh->fent, NULL ); fh->fent->size = ((unsigned)(offset + buflen) > fh->fent->size ? offset + buflen : fh->fent->size); fskit_entry_unlock( fh->fent ); } fskit_file_handle_unlock( fh ); return num_written; }
// i/o continuation, called with the same locks held as the trunc() static int fskit_trunc_cont( struct fskit_core* core, struct fskit_entry* fent, off_t new_size, ssize_t trunc_rc ) { if( trunc_rc == 0 ) { // update metadata fskit_entry_set_mtime( fent, NULL ); fskit_entry_set_atime( fent, NULL ); fent->size = new_size; } return 0; }
// continuation for successful write // fent must be write-locked int fskit_write_cont( struct fskit_core* core, struct fskit_entry* fent, off_t offset, ssize_t num_written ) { if( num_written >= 0 ) { fskit_entry_set_mtime( fent, NULL ); fskit_entry_set_atime( fent, NULL ); off_t size_delta = 0; if( offset + num_written > fent->size ) { size_delta = (offset + num_written) - fent->size; } fent->size += size_delta; } return 0; }
// 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; }