コード例 #1
0
ファイル: readlink.c プロジェクト: PeteGozz/fskit
// read the target in a symlink (including the null character at the end)
// return the number of bytes read on success (up to MIN(buflen, linkpath size))
// return -EINVAL if the entry isn't a symlink
// return -EIO if the entry is corrupt (indicates a bug in fskit)
// return -errno if path resolution fails
ssize_t fskit_readlink( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, char* buf, size_t buflen ) {

   int err = 0;
   ssize_t num_read = 0;
   struct fskit_entry* fent = NULL;

   // find this
   fent = fskit_entry_resolve_path( core, path, user, group, false, &err );
   if( err != 0 || fent == NULL ) {
      return err;
   }

   // symlink?
   if( fent->type != FSKIT_ENTRY_TYPE_LNK ) {
      fskit_entry_unlock( fent );
      return -EINVAL;
   }

   // sanity check
   if( fent->symlink_target == NULL ) {

      fskit_error("BUG: fskit entry %" PRIX64 " (at %p) is a symlink, but has no target path set\n", fent->file_id, fent );
      fskit_entry_unlock( fent );
      return -EIO;
   }

   // read it (including null character)
   num_read = (ssize_t)MIN( buflen, (unsigned)fent->size + 1 );

   memcpy( buf, fent->symlink_target, num_read );

   fskit_entry_unlock( fent );
   return num_read;
}
コード例 #2
0
ファイル: open.c プロジェクト: PeteGozz/fskit
// 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;
}
コード例 #3
0
ファイル: impl.cpp プロジェクト: iychoi/syndicate
// 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;
}
コード例 #4
0
ファイル: access.c プロジェクト: PeteGozz/fskit
int fskit_access( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, mode_t mode ) {

   int err = 0;
   struct fskit_entry* fent = fskit_entry_resolve_path( core, path, user, group, false, &err );
   if( !fent || err ) {
      if( !err ) {
         err = -ENOMEM;
      }
      return err;
   }

   // F_OK implicitly satisfied
   // give the application a chance to process the stat buffer
   struct stat sb;

   err = fskit_entry_fstat( fent, &sb );
   if( err == 0 ) {

      // check against stat buffer
      if( (mode & R_OK) && !FSKIT_ENTRY_IS_READABLE( sb.st_mode, sb.st_uid, sb.st_gid, user, group ) ) {
         err = -EACCES;
      }
      else if( (mode & W_OK) && !FSKIT_ENTRY_IS_WRITEABLE( sb.st_mode, sb.st_uid, sb.st_gid, user, group ) ) {
         err = -EACCES;
      }
      else if( (mode & X_OK) && !FSKIT_ENTRY_IS_EXECUTABLE( sb.st_mode, sb.st_uid, sb.st_gid, user, group ) ) {
         err = -EACCES;
      }
   }

   fskit_entry_unlock( fent );
   return err;
}
コード例 #5
0
ファイル: write.c プロジェクト: PeteGozz/fskit
// 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;
}
コード例 #6
0
ファイル: trunc.c プロジェクト: jaromil/fskit
// 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;
}
コード例 #7
0
ファイル: stat.c プロジェクト: jaromil/fskit
// stat an inode directly
// fill in the stat buffer, and call the user route
// NOTE: fent must NOT be locked, but it must be ref'ed
int fskit_fstat( struct fskit_core* core, char const* fs_path, struct fskit_entry* fent, struct stat* sb ) {
   
   // fill in defaults
   fskit_entry_rlock( fent );
   fskit_entry_fstat( fent, sb );
   fskit_entry_unlock( fent );
   
   // route to user callback
   int rc = fskit_do_user_stat( core, fs_path, fent, sb );

   return rc;
}
コード例 #8
0
ファイル: chown.c プロジェクト: PeteGozz/fskit
// change the owner of a path.
// the file must be owned by the given user.
// NOTE: no ingroup-checking occurs--if the caller is the owner, the new_group can be arbitrary.
// the caller should verify that the new_group is allowed by its security model.
// Return 0 on success, negative on error:
// -ENOMEM if oom
// -EPERM if the caller doesn't own the file
// -EACCES if the caller can't search a component of the path
// -ENOTDIR if the path has a parent non-directory
// -ENOENT if the entry doesn't exist
int fskit_chown( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, uint64_t new_user, uint64_t new_group ) {

   int err = 0;

   struct fskit_entry* fent = fskit_entry_resolve_path( core, path, user, group, true, &err );
   if( !fent || err ) {
      if( !err ) {
         err = -ENOMEM;
      }
      return err;
   }

   // can't chown unless we own the file
   if( fent->owner != user ) {
      fskit_entry_unlock( fent );
      return -EPERM;
   }

   fskit_entry_set_owner_and_group( fent, new_user, new_group );

   fskit_entry_unlock( fent );

   return err;
}
コード例 #9
0
ファイル: listxattr.c プロジェクト: jaromil/fskit
int fskit_listxattr( struct fskit_core* core, char const* path, uint64_t user, uint64_t group, char* list, size_t size ) {

   int err = 0;
   int rc = 0;

   // get the fent
   struct fskit_entry* fent = fskit_entry_resolve_path( core, path, user, group, false, &err );
   if( fent == NULL || err != 0 ) {
      return err;
   }

   // get the xattr
   rc = fskit_flistxattr( core, fent, list, size );

   fskit_entry_unlock( fent );

   return rc;
}
コード例 #10
0
ファイル: impl.cpp プロジェクト: iychoi/syndicate
// stat a file's block--build a manifest request, and set its mode
// return 0 on success 
// return -ESTALE if the inode is not local 
// return -ENOENT if we don't have it
// return -ENOMEM on OOM
// return -errno on error 
static int UG_impl_stat_block( struct SG_gateway* gateway, struct SG_request_data* reqdat, struct SG_request_data* entity_info, mode_t* mode, void* cls ) {
  
   int rc = 0;
   struct UG_state* ug = (struct UG_state*)SG_gateway_cls( gateway );
   int64_t block_version = 0;
   UG_handle_t* fi = NULL;
   struct fskit_entry* fent = NULL;
   struct UG_inode* inode = NULL;
   uint64_t file_id = 0;
   int64_t file_version = 0;
   int close_rc = 0;

   fi = UG_open( ug, reqdat->fs_path, O_RDONLY, &rc );
   if( fi == NULL ) {

      SG_error("UG_open('%s') rc = %d\n", reqdat->fs_path, rc );
      return rc;
   }

   fskit_file_handle_rlock( fi->fh );
   
   fent = fskit_file_handle_get_entry( fi->fh );
   if( fent == NULL ) {
      SG_error("BUG: no entry for handle %p\n", fi->fh );
      exit(1);
   }

   fskit_entry_rlock( fent );
   inode = (struct UG_inode*)fskit_entry_get_user_data( fent );
   if( inode == NULL ) {
      SG_error("BUG: no inode for entry %p\n", fent );
      exit(1);
   }

   if( UG_inode_coordinator_id( inode ) != SG_gateway_id( gateway ) ) {

      // not ours 
      SG_error("Not the coordinator of '%s' (it is now %" PRIu64 ")\n", reqdat->fs_path, UG_inode_coordinator_id( inode ) );
      fskit_entry_unlock( fent );
      fskit_file_handle_unlock( fi->fh );

      rc = UG_close( ug, fi );
      if( rc != 0 ) {

         SG_error("UG_close('%s') rc = %d\n", reqdat->fs_path, rc );
      }
      return rc;
   }

   file_id = UG_inode_file_id( inode );
   file_version = UG_inode_file_version( inode );

   if( mode != NULL ) {
      *mode = fskit_entry_get_mode( fent );
   }
   if( entity_info != NULL ) {
      rc = UG_getblockinfo( ug, reqdat->block_id, &block_version, NULL, fi );
   }

   fskit_entry_unlock( fent );
   fskit_file_handle_unlock( fi->fh );
   inode = NULL;

   if( rc != 0 ) {

      SG_error("UG_getblockinfo(%s[%" PRIu64 "]) rc = %d\n", reqdat->fs_path, reqdat->block_id, rc);
      goto UG_impl_stat_block_out;
   }

   rc = SG_request_data_init_block( gateway, reqdat->fs_path, file_id, file_version, reqdat->block_id, block_version, entity_info );
   if( rc != 0 ) {

      SG_error("SG_request_data_init_block rc = %d\n", rc );
      goto UG_impl_stat_block_out;
   }

UG_impl_stat_block_out:

   close_rc = UG_close( ug, fi );
   if( close_rc != 0 ) {

      SG_error("UG_close('%s') rc = %d\n", reqdat->fs_path, close_rc );
   }

   return rc;
}
コード例 #11
0
ファイル: closedir.c プロジェクト: jaromil/fskit
// close a directory handle, freeing it.
// a directory may be unlinked on close, if this was the last handle to it, and its link count was zero.
// if this happens, then app_dir_data will contain the directory's app data, and the directory will be freed.
// return 0 on success
// return the following errors:
// * EBADF if the directory handle is invalid
// * EDEADLK if there is a bug in the lock handling
int fskit_closedir( struct fskit_core* core, struct fskit_dir_handle* dirh ) {

   int rc = 0;

   rc = fskit_dir_handle_wlock( dirh );
   if( rc != 0 ) {
      // indicates deadlock; shouldn't happen
      fskit_error("BUG: fskit_dir_handle_wlock(%p) rc = %d\n", dirh, rc );
      return rc;
   }

   if( dirh->dent == NULL ) {

      fskit_dir_handle_unlock( dirh );
      return -EBADF;
   }

   // run user-given close route.  Note that this may unlock dirh->dent and re-lock it, but only if it is fully unlinked.
   rc = fskit_run_user_close( core, dirh->path, dirh->dent, dirh->app_data );
   if( rc != 0 ) {

      fskit_error("fskit_run_user_close(%s) rc = %d\n", dirh->path, rc );
      
      fskit_dir_handle_unlock( dirh );
      return rc;
   }

   rc = fskit_entry_wlock( dirh->dent );
   if( rc != 0 ) {

      // shouldn't happen; indicates deadlock
      fskit_error("BUG: fskit_entry_wlock(%p) rc = %d\n", dirh->dent, rc );

      fskit_dir_handle_unlock( dirh );
      return rc;
   }
   
   // no longer open
   dirh->dent->open_count--;

   // see if we can destroy this....
   // NOTE: this may unlock and free dirh->dent
   rc = fskit_entry_try_destroy_and_free( core, dirh->path, dirh->dent );
   if( rc > 0 ) {

      // dent was unlocked and destroyed
      dirh->dent = NULL;
      rc = 0;
   }
   if( rc < 0 ) {

      // some error occurred
      fskit_error("fskit_entry_try_destroy(%p) rc = %d\n", dirh->dent, rc );
      fskit_entry_unlock( dirh->dent );

      return rc;
   }
   else {
      
      // not destroyed.
      // done with this directory
      fskit_entry_unlock( dirh->dent );
   }

   // get rid of this handle
   fskit_dir_handle_unlock( dirh );
   fskit_dir_handle_destroy( dirh );

   return 0;
}
コード例 #12
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// load the filesystem with metadata from under the mountpoint
// return 0 on success 
// return -ENOMEM on OOM
static int vdevfs_dev_import( struct fskit_fuse_state* fs, void* arg ) {
   
   struct vdevfs* vdev = (struct vdevfs*)arg;
   int rc = 0;
   queue<int> dirfds;           // directory descriptors
   queue<char*> dirpaths;       // relative paths to the mountpoint 
   struct fskit_entry* dir_ent = NULL;
   
   struct vdevfs_scandirat_context scan_context;
   
   char* root = vdev_strdup_or_null("/");
   if( root == NULL ) {
      return -ENOMEM;
   }
   
   int root_fd = dup( vdev->mountpoint_dirfd );
   if( root_fd < 0 ) {
      
      vdev_error("dup(%d) rc = %d\n", vdev->mountpoint_dirfd, rc );
      rc = -errno;
      free( root );
      return rc;
   }
   
   // start at the mountpoint
   try {
      
      dirfds.push( root_fd );
      dirpaths.push( root );
   }
   catch( bad_alloc& ba ) {
      
      free( root );
      return -ENOMEM;
   }
   
   while( dirfds.size() > 0 ) {
      
      // next directory 
      int dirfd = dirfds.front();
      char* dirpath = dirpaths.front();
      
      dirfds.pop();
      dirpaths.pop();
      
      // look up this entry 
      dir_ent = fskit_entry_resolve_path( fskit_fuse_get_core( vdev->fs ), dirpath, 0, 0, true, &rc );
      if( rc != 0 ) {
         
         // shouldn't happen--we're going breadth-first 
         vdev_error("fskit_entry_resolve_path('%s') rc = %d\n", dirpath, rc );
         
         close( dirfd );
         free( dirpath );
         dirpath = NULL;
        
         break;
      }
      
      // make a scan context 
      vdevfs_scandirat_context_init( &scan_context, fskit_fuse_get_core( vdev->fs ), dir_ent, dirpath, &dirfds, &dirpaths );
      
      // scan this directory 
      rc = vdev_load_all_at( dirfd, vdevfs_scandirat_context_callback, &scan_context );
      
      fskit_entry_unlock( dir_ent );
      
      if( rc != 0 ) {
         
         // failed
         vdev_error("vdev_load_all_at(%d, '%s') rc = %d\n", dirfd, dirpath, rc );
      }
      
      close( dirfd );
      free( dirpath );
      dirpath = NULL;
      
      if( rc != 0 ) {
         break;
      }
   }
   
   // free any remaining directory state 
   size_t num_dirfds = dirfds.size();
   for( unsigned int i = 0; i < num_dirfds; i++ ) {
      
      int next_dirfd = dirfds.front();
      dirfds.pop();
      
      close( next_dirfd );
   }
   
   size_t num_dirpaths = dirpaths.size();
   for( unsigned int i = 0; i < num_dirpaths; i++ ) {
      
      char* path = dirpaths.front();
      dirpaths.pop();
      
      free( path );
   }
   
   return rc;
}
コード例 #13
0
ファイル: fs.cpp プロジェクト: 8l/vdev
// readdir: equivocate about which devices exist, depending on who's asking
// omit entries if the ACLs forbid them
int vdevfs_readdir( struct fskit_core* core, struct fskit_match_group* grp, struct fskit_entry* fent, struct fskit_dir_entry** dirents, size_t num_dirents ) {
   
   int rc = 0;
   struct fskit_entry* child = NULL;
   
   // entries to omit in the listing
   vector<int> omitted_idx;
   
   pid_t pid = 0;
   uid_t uid = 0;
   gid_t gid = 0;
   
   struct vdevfs* vdev = (struct vdevfs*)fskit_core_get_user_data( core );
   struct fskit_fuse_state* fs_state = fskit_fuse_get_state();
   
   struct stat sb;
   struct pstat ps;
   char* child_path = NULL;
   
   pid = fskit_fuse_get_pid();
   uid = fskit_fuse_get_uid( fs_state );
   gid = fskit_fuse_get_gid( fs_state );
   
   vdev_debug("vdevfs_readdir(%s, %zu) from user %d group %d task %d\n", grp->path, num_dirents, uid, gid, pid );
   
   // see who's asking
   rc = pstat( pid, &ps, 0 );
   if( rc != 0 ) { 
      
      vdev_error("pstat(%d) rc = %d\n", pid, rc );
      return -EIO;
   }
   
   for( unsigned int i = 0; i < num_dirents; i++ ) {
      
      // skip . and ..
      if( strcmp(dirents[i]->name, ".") == 0 || strcmp(dirents[i]->name, "..") == 0 ) {
         continue;
      }
      
      // find the associated fskit_entry
      child = fskit_dir_find_by_name( fent, dirents[i]->name );
      
      if( child == NULL ) {
         // strange, shouldn't happen...
         continue;
      }
      
      fskit_entry_rlock( child );
      
      // construct a stat buffer from what we actually need 
      memset( &sb, 0, sizeof(struct stat) );
      
      sb.st_uid = child->owner;
      sb.st_gid = child->group;
      sb.st_mode = fskit_fullmode( child->type, child->mode );
      
      child_path = fskit_fullpath( grp->path, child->name, NULL );
      if( child_path == NULL ) {
         
         // can't continue; OOM
         fskit_entry_unlock( child );
         rc = -ENOMEM;
         break;
      }
      
      // filter it 
      rc = vdev_acl_apply_all( vdev->config, vdev->acls, vdev->num_acls, child_path, &ps, uid, gid, &sb );
      if( rc < 0 ) {
         
         vdev_error("vdev_acl_apply_all('%s', uid=%d, gid=%d, pid=%d) rc = %d\n", child_path, uid, gid, pid, rc );
         rc = -EIO;
      }
      else if( rc == 0 || (sb.st_mode & 0777) == 0 ) {
         
         // omit this one 
         vdev_debug("Filter '%s'\n", child->name );
         omitted_idx.push_back( i );
         
         rc = 0;
      }
      else {
         
         // success; matched
         rc = 0;
      }
      
      fskit_entry_unlock( child );
      
      free( child_path );
      
      // error?
      if( rc != 0 ) {
         break;
      }
   }
   
   // skip ACL'ed entries
   for( unsigned int i = 0; i < omitted_idx.size(); i++ ) {
      
      fskit_readdir_omit( dirents, omitted_idx[i] );
   }
   
   return rc;
}
コード例 #14
0
ファイル: open.c プロジェクト: PeteGozz/fskit
// 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;
}
コード例 #15
0
ファイル: opendir.c プロジェクト: PeteGozz/fskit
// 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;
}
コード例 #16
0
ファイル: mkdir.c プロジェクト: PeteGozz/fskit
// 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 );
      }
   }