// 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; }
// update a file's manifest, in response to a remote call // return 0 on success // return -ENOENT if not found // return -ESTALE if not local // return -errno on error // NOTE: the permissions will already have been checked by the server static int UG_impl_manifest_patch( struct SG_gateway* gateway, struct SG_request_data* reqdat, struct SG_manifest* write_delta, void* cls ) { int rc = 0; int ref_rc = 0; struct fskit_entry* fent = NULL; struct UG_state* ug = (struct UG_state*)SG_gateway_cls( gateway ); struct fskit_core* fs = UG_state_fs( ug ); struct UG_inode* inode = NULL; struct ms_client* ms = SG_gateway_ms( gateway ); uint64_t volume_id = ms_client_get_volume_id( ms ); rc = UG_consistency_path_ensure_fresh( gateway, reqdat->fs_path ); if( rc != 0 ) { SG_error("UG_consistency_path_ensure_fresh('%s') rc = %d\n", reqdat->fs_path, rc ); return rc; } rc = UG_consistency_manifest_ensure_fresh( gateway, reqdat->fs_path ); if( rc != 0 ) { SG_error("UG_consistency_manifest_ensure_fresh('%s') rc = %d\n", reqdat->fs_path, rc ); return rc; } // look up fent = fskit_entry_resolve_path( fs, reqdat->fs_path, reqdat->user_id, volume_id, true, &rc ); if( fent == NULL ) { return rc; } inode = (struct UG_inode*)fskit_entry_get_user_data( fent ); // must be coordinated by us if( UG_inode_coordinator_id( inode ) != SG_gateway_id( gateway ) ) { fskit_entry_unlock( fent ); return -ESTALE; } // update the manifest fskit_entry_ref_entry( fent ); rc = UG_write_patch_manifest( gateway, reqdat, inode, write_delta ); fskit_entry_unlock( fent ); ref_rc = fskit_entry_unref( fs, reqdat->fs_path, fent ); if( ref_rc != 0 ) { SG_warn("fskit_entry_unref('%s') rc = %d\n", reqdat->fs_path, rc ); } return rc; }
// stat a path. // fill in the stat buffer on success. // return the usual path resolution errors. int fskit_stat( struct fskit_core* core, char const* fs_path, uint64_t user, uint64_t group, struct stat* sb ) { int rc = 0; // ref this entry, so it won't disappear on stat struct fskit_entry* fent = fskit_entry_ref( core, fs_path, &rc ); if( fent == NULL ) { return rc; } // stat it rc = fskit_fstat( core, fs_path, fent, sb ); fskit_entry_unref( core, fs_path, fent ); 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; }