// open a directory, but fail-fast if we can't get path metadata struct fs_dir_handle* fs_entry_opendir( struct fs_core* core, char const* fs_path, uint64_t user, uint64_t vol, int* err ) { // ensure path ends in / char path[PATH_MAX]; strcpy( path, fs_path ); md_sanitize_path( path ); int rc = fs_entry_revalidate_path( core, path ); if( rc != 0 ) { SG_error("fs_entry_revalidate_path(%s) rc = %d\n", path, rc ); return NULL; } uint64_t parent_id = 0; char* parent_name = NULL; struct fs_entry* dir = fs_entry_resolve_path_and_parent_info( core, path, user, vol, true, err, &parent_id, &parent_name ); if( dir == NULL ) { return NULL; } // make sure it's a directory if( dir->ftype != FTYPE_DIR ) { *err = -ENOTDIR; fs_entry_unlock( dir ); return NULL; } // open this directory dir->open_count++; struct fs_dir_handle* dirh = fs_dir_handle_create( dir, path, parent_id, parent_name ); rc = fs_dir_handle_open( dirh ); if( rc != 0 ) { fs_dir_handle_destroy( dirh ); free( dirh ); dirh = NULL; *err = rc; } // release the directory fs_entry_unlock( dir ); free( parent_name ); return dirh; }
// remove a directory, if it is empty int fs_entry_rmdir( struct fs_core* core, char const* path, uint64_t user, uint64_t volume ) { if( core->gateway == GATEWAY_ANON ) { errorf("%s", "Removing directories is forbidden for anonymous gateways\n"); return -EPERM; } // get some info about this directory first int rc = 0; char* fpath = strdup( path ); md_sanitize_path( fpath ); // revalidate this path rc = fs_entry_revalidate_path( core, volume, fpath ); if( rc != 0 && rc != -ENOENT ) { // consistency cannot be guaranteed errorf("fs_entry_revalidate_path(%s) rc = %d\n", fpath, rc ); free( fpath ); return rc; } free( fpath ); int err = 0; struct fs_entry* dent = fs_entry_resolve_path( core, path, user, volume, false, &err ); if( !dent || err ) { return err; } if( dent->ftype != FTYPE_DIR ) { fs_entry_unlock( dent ); return -ENOTDIR; } char* path_dirname = md_dirname( path, NULL ); struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, volume, true, &err ); free( path_dirname ); if( !parent || err ) { fs_entry_unlock( dent ); return err; } // IS THE PARENT EMPTY? if( fs_entry_set_count( dent->children ) > 2 ) { // nope fs_entry_unlock( dent ); fs_entry_unlock( parent ); return -ENOTEMPTY; } struct md_entry ent; fs_entry_to_md_entry( core, &ent, dent, parent->file_id, parent->name ); // tell the MS that this directory should go away rc = ms_client_delete( core->ms, &ent ); md_entry_free( &ent ); if( rc != 0 ) { errorf( "ms_client_delete(%s) rc = %d\n", path, rc ); rc = -EREMOTEIO; fs_entry_unlock( dent ); fs_entry_unlock( parent ); } else { fs_entry_unlock( dent ); // detach from the filesystem rc = fs_entry_detach_lowlevel( core, parent, dent ); if( rc != 0 ) { errorf("fs_entry_detach_lowlevel(%s) rc = %d\n", path, rc ); } fs_entry_unlock( parent ); } return rc; }
// Try to open a file, but fail-fast on error. It behaves as close to POSIX-open as possible, with the following differences: // * return -EREMOTEIO if the UG could not contact the MS, or if it could not obtain a fresh manifest. // * return -EUCLEAN if the UG was unable to merge metadata from the MS into its metadata hierarchy (usually indicates a bug) // * return a driver-specific, non-zero error code given by the driver's create_file() method // Side-effects: // * re-downloads and updates metadata for all entries along the path that are stale. // * re-downloads the manifest for the i-node if it is stale. struct fs_file_handle* fs_entry_open( struct fs_core* core, char const* _path, uint64_t user, uint64_t vol, int flags, mode_t mode, int* err ) { // sanity check if( (flags & O_RDONLY) == 0 && (flags & O_RDWR) != 0 && (flags & O_WRONLY) != 0 ) { *err = -EINVAL; return NULL; } if( (flags & O_RDONLY) != 0 && (flags & O_WRONLY) != 0 ) { *err = -EINVAL; return NULL; } // sanity check: check open mode vs whether or not we're a client and/or have read-only caps if( core->gateway == SG_GATEWAY_ANON ) { // no authentication; we're read-only if( flags & (O_CREAT | O_RDWR | O_WRONLY | O_TRUNC | O_EXCL) ) { SG_error("%s", "Opening to create, write, or truncate is forbidden for anonymous gateways\n"); *err = -EPERM; return NULL; } } int rc = 0; char* parent_name = NULL; uint64_t parent_id = 0; struct fs_entry* child = NULL; struct fs_entry* parent = NULL; char const* reval_method = NULL; struct fs_file_handle* ret = NULL; // make sure path is sane char* path = strdup(_path); md_sanitize_path( path ); // revalidate metadata if( flags & O_CREAT ) { reval_method = "fs_entry_create_revalidate"; rc = fs_entry_create_revalidate( core, path, user, vol ); } else { reval_method = "fs_entry_open_revalidate"; rc = fs_entry_open_revalidate( core, path, user, vol ); } if( rc != 0 ) { SG_error("%s(%s) rc = %d\n", reval_method, path, rc ); *err = rc; free( path ); return NULL; } // get the parent and child // NOTE: parent will be write-locked; child will not be rc = fs_entry_open_parent_and_child( core, path, user, vol, &parent, &child ); if( rc != 0 ) { SG_error("fs_entry_open_parent_and_child( %s ) rc = %d\n", path, rc ); *err = rc; free( path ); return NULL; } if( flags & O_CREAT ) { // creating... if( child != NULL ) { // can't create--child exists *err = -EEXIST; free( path ); return NULL; } // carry out the local create rc = fs_entry_do_create( core, path, parent, &child, user, vol, mode ); if( rc != 0 ) { SG_error("fs_entry_do_create( %s ) rc = %d\n", path, rc ); *err = rc; free( path ); return NULL; } // preserve these before unlocking, since we'll need them for the file handle parent_id = parent->file_id; parent_name = strdup( parent->name ); fs_entry_wlock( child ); fs_entry_unlock( parent ); // carry out the remote create rc = fs_entry_do_MS_create( core, path, child, parent_id, parent_name ); if( rc != 0 ) { SG_error("fs_entry_do_MS_create(%s) rc = %d\n", path, rc ); if( rc == -EAGAIN ) { *err = rc; } else { *err = -EREMOTEIO; } // NOTE: parent is guaranteed to exist, since child is attached to it and is write-locked (so it can't be unlinked) fs_entry_wlock( parent ); fs_entry_undo_create( core, path, parent, child ); fs_entry_unlock( parent ); child = NULL; free( path ); free( parent_name ); return NULL; } } else { // opening... if( child == NULL ) { fs_entry_unlock( parent ); // can't open--child doesn't exist *err = -ENOENT; free( path ); return NULL; } // preserve these before unlocking, since we'll need them for the file handle parent_id = parent->file_id; parent_name = strdup( parent->name ); fs_entry_wlock( child ); fs_entry_unlock( parent ); // carry out the open rc = fs_entry_do_open( core, path, child, user, vol, flags ); if( rc != 0 ) { fs_entry_unlock( child ); SG_error("fs_entry_do_open(%s) rc = %d\n", path, rc ); *err = rc; free( path ); free( parent_name ); return NULL; } // if we're truncating, do so as well if( flags & O_TRUNC ) { rc = fs_entry_open_truncate( core, path, child, parent_id, parent_name ); if( rc != 0 ) { fs_entry_unlock( child ); SG_error("fs_entry_open_truncate(%s) rc = %d\n", path, rc ); *err = rc; free( path ); free( parent_name ); return NULL; } } } // success! child->atime = md_current_time_seconds(); // give back a file handle ret = fs_file_handle_create( core, child, path, parent_id, parent_name ); fs_file_handle_open( ret, flags, mode ); fs_entry_unlock( child ); free( path ); free( parent_name ); return ret; }