Example #1
0
// detach a file from the filesystem
// Only remove a directory if it is empty.
int fs_entry_detach( struct fs_core* core, char const* path, uint64_t user, uint64_t vol ) {

   // resolve the parent of this child (and write-lock it)
   char* path_dirname = md_dirname( path, NULL );
   char* path_basename = md_basename( path, NULL );
   int err = 0;
   struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, vol, true, &err );
   free( path_dirname );

   if( parent == NULL ) {
      free( path_basename );
      return err;
   }
   if( parent->ftype != FTYPE_DIR ) {
      // not a directory
      fs_entry_unlock( parent );
      free( path_basename );
      return -ENOTDIR;
   }


   if( !IS_DIR_READABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
      // directory not searchable
      fs_entry_unlock( parent );
      free( path_basename );
      return -EACCES;
   }
   
   // is parent writeable?
   if( !IS_WRITEABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
      // nope
      fs_entry_unlock( parent );
      free( path_basename );
      return -EACCES;
   }

   struct fs_entry* child = fs_entry_set_find_name( parent->children, path_basename );
   free( path_basename );

   if( child == NULL ) {
      // doesn't exist
      fs_entry_unlock( parent );
      return -ENOENT;
   }

   int rc = fs_entry_detach_lowlevel( core, parent, child );

   fs_entry_unlock( parent );

   return rc;
}
Example #2
0
// attach a file to the filesystem (same as link())
// THIS METHOD ONLY UPDATES THE METADATA; IT DOES NOT TOUCH STABLE STORAGE
int fs_entry_attach( struct fs_core* core, struct fs_entry* fent, char const* path, uint64_t user, uint64_t vol ) {
   // sanity check: path's basename should be fent's name
   char* path_base = md_basename( path, NULL );
   if( strcmp( fent->name, path_base ) != 0 ) {
      free(path_base);
      return -EINVAL;      // invalid entry
   }
   free( path_base );

   int err = 0;
   char* dirname = md_dirname( path, NULL );
   struct fs_entry* parent = fs_entry_resolve_path( core, dirname, user, vol, true, &err );
   free( dirname );
   if( parent == NULL ) {
      return err;
   }

   err = 0;

   fs_entry_wlock( fent );

   if( !IS_DIR_READABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
      // directory not searchable
      fs_entry_unlock( fent );
      fs_entry_unlock( parent );
      return -EACCES;
   }
   if( !IS_WRITEABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
      // not writable--cannot insert
      fs_entry_unlock( fent );
      fs_entry_unlock( parent );
      return -EACCES;
   }
   if( fs_entry_set_find_name( parent->children, fent->name ) == NULL ) {
      fs_entry_attach_lowlevel( core, parent, fent );
   }
   else {
      err = -EEXIST;
   }

   fs_entry_unlock( fent );

   fs_entry_unlock( parent );

   return err;
}
Example #3
0
// revalidate on create (not open)
int fs_entry_create_revalidate( struct fs_core* core, char const* path, uint64_t user, uint64_t vol ) {

    int rc = 0;
    char* parent_path = md_dirname( path, NULL );

    // see that the parent still exists
    rc = fs_entry_revalidate_path( core, parent_path );
    if( rc != 0 ) {
        SG_error("fs_entry_revalidate_path(%s) rc = %d\n", parent_path, rc );

        free( parent_path );
        return rc;
    }

    free( parent_path );
    return rc;
}
Example #4
0
// get the parent and child nodes on create/open, checking permissions along the way
// write-lock the parent.
// do NOT touch the child
// if the child is not found, *child will be set to NULL
// return 0 on success
// return -ENOTDIR if a directory along the path wasn't a directory
// return -EACCES on permission error
int fs_entry_open_parent_and_child( struct fs_core* core, char const* path, uint64_t user, uint64_t vol, struct fs_entry** ret_parent, struct fs_entry** ret_child ) {

    // resolve the parent of this child (and write-lock it)
    int rc = 0;
    char* path_dirname = md_dirname( path, NULL );
    char* path_basename = md_basename( path, NULL );

    struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, vol, true, &rc );

    if( parent == NULL ) {

        free( path_basename );
        free( path_dirname );

        return rc;
    }

    free( path_dirname );

    if( parent->ftype != FTYPE_DIR ) {
        // parent is not a directory
        fs_entry_unlock( parent );
        free( path_basename );

        return -ENOTDIR;
    }

    // can parent be searched?
    if( !IS_DIR_READABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
        // nope
        fs_entry_unlock( parent );
        free( path_basename );

        return -EACCES;
    }

    // resolve the child
    struct fs_entry* child = fs_entry_set_find_name( parent->children, path_basename );

    free( path_basename );

    *ret_parent = parent;
    *ret_child = child;

    return 0;
}
Example #5
0
// 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;
}
Example #6
0
// unlink a file from the filesystem
// pass -1 if the version is not known, or pass the known version to be unlinked
// return -EUCLEAN if we failed to garbage-collect, but needed to (i.e. a manifest was missing)
// return -EREMOTEIO for failure to revalidate metadata 
// return -ESTALE if the given information is out of date
int fs_entry_versioned_unlink( struct fs_core* core, char const* path, uint64_t file_id, uint64_t coordinator_id, int64_t known_version, uint64_t owner, uint64_t volume, uint64_t gateway_id, 
                               bool check_file_id_and_coordinator_id ) {
   
   // can't modify state if anonymous
   if( core->gateway == SG_GATEWAY_ANON ) {
      SG_error("%s", "Writing is forbidden for anonymous gateways\n");
      return -EPERM;
   }
   
   // get some info about this file first
   int rc = 0;
   int err = 0;
   bool no_manifest = false;
   
   // consistency check
   err = fs_entry_revalidate_path( core, path );
   if( err != 0 ) {
      SG_error("fs_entry_revalidate_path(%s) rc = %d\n", path, err );
      
      if( err == -ENOENT )
         return -ENOENT;
      
      return -EREMOTEIO;
   }
   
   // look up the parent
   char* path_dirname = md_dirname( path, NULL );
   char* path_basename = md_basename( path, NULL );
   
   struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, owner, volume, true, &err );

   free( path_dirname );

   if( !parent || err ) {

      free( path_basename );
      return err;
   }
   
   if( parent->ftype != FTYPE_DIR ) {
      fs_entry_unlock( parent );
      free( path_basename );
      return err;
   }
   
   // get the child
   struct fs_entry* fent = fs_entry_set_find_name( parent->children, path_basename );
   
   free( path_basename );
   
   if( fent == NULL ) {
      fs_entry_unlock( fent );
      fs_entry_unlock( parent );
      return -ENOENT;
   }
   
   fs_entry_wlock( fent );
   
   bool local = FS_ENTRY_LOCAL( core, fent );
   int64_t version = fent->version;
   
   if( check_file_id_and_coordinator_id ) {
      if( fent->file_id != file_id ) {
         SG_error("Remote unlink to file %s ID %" PRIX64 ", expected %" PRIX64 "\n", path, file_id, fent->file_id );
         fs_entry_unlock( fent );
         fs_entry_unlock( parent );
         return -ESTALE;
      }
      
      if( fent->coordinator != coordinator_id ) {
         SG_error("Remote unlink to file %s coordinator %" PRIu64 ", expected %" PRIu64 "\n", path, coordinator_id, fent->coordinator );
         fs_entry_unlock( fent );
         fs_entry_unlock( parent );
         return -ESTALE;
      }
   }
   
   if( known_version > 0 && fent->version > 0 && fent->version != known_version ) {
      SG_error("Remote unlink to file %s version %" PRId64 ", expected %" PRId64 "\n", path, known_version, fent->version );
      fs_entry_unlock( fent );
      fs_entry_unlock( parent );
      return -ESTALE;
   }
   
   // make sure the manifest is fresh, so we delete every block
   // only need to worry about this if file has > 0 size
   if( fent->size > 0 ) {
      
      // try to get it
      err = fs_entry_revalidate_manifest( core, path, fent );
            
      if( err != 0 ) {
         SG_error( "fs_entry_revalidate_manifest(%s) rc = %d\n", path, err );
         
         if( err == -ENOENT ) {
            // continue without a manifest 
            no_manifest = true;
            SG_error("WARN: no manifest found for %s %" PRIX64 ".  Assuming data is already vacuumed.\n", path, fent->file_id );
         }
         else {
            // some other problem
            fs_entry_unlock( fent );
            fs_entry_unlock( parent );
            return err;
         }
      }
   }
   
   // tell the driver we're deleting 
   int driver_rc = driver_delete_file( core, core->closure, path, fent );
   if( driver_rc != 0 ) {
      SG_error("driver_delete_file(%s %" PRIX64 ") rc = %d\n", path, fent->file_id, driver_rc );
      
      fs_entry_unlock( fent );
      fs_entry_unlock( parent );
      return driver_rc;
   }
   
   rc = 0;
   
   if( !local ) {
      // this is someone else's file; tell them to unlink
      Serialization::WriteMsg* detach_request = new Serialization::WriteMsg();

      fs_entry_init_write_message( detach_request, core, Serialization::WriteMsg::DETACH );
      
      fs_entry_prepare_detach_message( detach_request, path, fent, version );

      Serialization::WriteMsg* detach_ack = new Serialization::WriteMsg();
      
      // send the write message, or become the coordinator
      rc = fs_entry_send_write_or_coordinate( core, path, fent, detach_request, detach_ack );
      
      if( rc < 0 ) {
         SG_error( "fs_entry_send_write_or_coordinate(%s) rc = %d\n", path, rc );
      }
      else if( rc == 0 ) {
         // successfully sent
         if( detach_ack->type() != Serialization::WriteMsg::ACCEPTED ) {
            if( detach_ack->type() == Serialization::WriteMsg::ERROR ) {
               // could not detach on the remote end
               SG_error( "remote unlink error = %d (%s)\n", detach_ack->errorcode(), detach_ack->errortxt().c_str() );
               rc = detach_ack->errorcode();
            }
            else {
               // unknown message
               SG_error( "remote unlink invalid message %d\n", detach_ack->type() );
               rc = -EIO;
            }
         }
      }
      else {
         // we're now the coordinator.          
         local = true;
      }

      delete detach_ack;
      delete detach_request;
   }

   if( local ) {
      // we're responsible for this file
      // mark the file as deleted, so it won't show up again in any listing 
      fent->deletion_in_progress = true;
      
      // safe to unlock parent--it won't be empty (in a rmdir-able sense) until fent is fully garbage-collected, but fent won't be listed either
      fs_entry_unlock( parent );
      
      // garbage-collect, then unlink on the MS.  Loop this until we succeed in unlinking on the MS (which can only happen 
      // once all of fent's data has been garbage-collected).
      while( true ) {
         
         if( !no_manifest ) {
            // if we got the latest manifest, garbage-collect all writes on the file 
            rc = fs_entry_vacuumer_file( core, path, fent );
            
            if( rc != 0 ) {
               SG_error("fs_entry_vacuumer_vacuum_file( %s %" PRIX64 " ) rc = %d\n", path, fent->file_id, rc );
               
               // failed to garbage-collect...need to un-delete fent
               fent->deletion_in_progress = false;
               fs_entry_unlock( fent );
               
               return -EREMOTEIO;
            }
         }
         
         // tell the metadata server we just unlinked
         // preserve the entry information so we can issue a deletion
         struct md_entry ent;
         fs_entry_to_md_entry( core, &ent, fent, parent->file_id, parent->name );

         rc = ms_client_delete( core->ms, &ent );
         md_entry_free( &ent );
            
         if( rc != 0 ) {
            SG_error( "ms_client_delete(%s) rc = %d\n", path, rc );
            
            if( rc == -EAGAIN ) {
               if( !no_manifest ) {
                  // try vacuuming again--some write got added in between our garbage-collection and our unlink request
                  rc = 0;
                  continue;
               }
               else {
                  // there are un-garbage-collected writes, but we have no manifest, so we can't vacuum in order to proceed with the delete.
                  SG_error("MEMORY LEAK DETECTED: No manifest for %" PRIX64 " available; unable to vacuum!\n", fent->file_id );
                  
                  // failed to garbage-collect...need to un-delete fent
                  fent->deletion_in_progress = false;
                  fs_entry_unlock( fent );
                  
                  return -EUCLEAN;
               }
            }
            else {
               
               // something more serious 
               rc = -EREMOTEIO;
               
               fent->deletion_in_progress = false;
               fs_entry_unlock( fent );
               return rc;
            }
         }
         else {
            // success!
            break;
         }
      }
      
      // re-lock the parent--it's guaranteed to exist, since it's not empty 
      fs_entry_wlock( parent );
      
      // unlock fent--we're done with it 
      fs_entry_unlock( fent );
      
      // detatch fent from parent
      rc = fs_entry_detach_lowlevel( core, parent, fent );
      if( rc != 0 ) {
         SG_error("fs_entry_detach_lowlevel(%" PRIX64 ") rc = %d\n", fent->file_id, rc );
         fs_entry_unlock( parent );
         
         return rc;
      }
      
      fs_entry_unlock( parent );
   }
   
   return rc;
}
Example #7
0
// make a node (regular files only at this time)
int fs_entry_mknod( struct fs_core* core, char const* path, mode_t mode, dev_t dev, uint64_t user, uint64_t vol ) {
    // only regular files at this time...
    if( ! ( S_ISREG( mode ) || S_ISFIFO( mode ) ) ) {
        return -ENOTSUP;
    }

    // revalidate this path
    int rc = fs_entry_revalidate_path( core, path );
    if( rc != 0 && rc != -ENOENT ) {
        // consistency cannot be guaranteed
        SG_error("fs_entry_revalidate_path(%s) rc = %d\n", path, rc );
        return rc;
    }

    int err = 0;

    // get the parent directory and lock it
    char* path_dirname = md_dirname( path, NULL );
    struct fs_entry* parent = fs_entry_resolve_path( core, path_dirname, user, vol, true, &err );
    free( path_dirname );

    if( !IS_DIR_READABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
        // not searchable
        fs_entry_unlock( parent );
        return -EACCES;
    }

    if( !IS_WRITEABLE( parent->mode, parent->owner, parent->volume, user, vol ) ) {
        // not writeable
        fs_entry_unlock( parent );
        return -EACCES;
    }

    uint64_t parent_id = parent->file_id;
    char* parent_name = strdup( parent->name );

    char* path_basename = md_basename( path, NULL );

    // make sure it doesn't exist already (or isn't in the process of being deleted, since we might have to re-create it if deleting it fails)
    if( fs_entry_set_find_name( parent->children, path_basename ) != NULL ) {
        free( path_basename );
        fs_entry_unlock( parent );
        free( parent_name );
        return -EEXIST;
    }

    struct fs_entry* child = (struct fs_entry*)calloc( sizeof(struct fs_entry), 1 );

    struct timespec ts;
    clock_gettime( CLOCK_REALTIME, &ts );
    int mmode = 0;
    if (S_ISFIFO(mode)) {
        mmode = ( mode & 0777 ) | S_IFIFO;
        err = fs_entry_init_fifo( core, child, path_basename, 0, fs_entry_next_file_version(), user, core->gateway, vol, mmode, 0, ts.tv_sec, ts.tv_nsec, 0, 0 );
    }
    if (S_ISREG(mode)) {
        mmode = ( mode & 0777 );
        err = fs_entry_init_file( core, child, path_basename, 0, fs_entry_next_file_version(), user, core->gateway, vol, mmode, 0, ts.tv_sec, ts.tv_nsec, 0, 0 );
    }

    if( err == 0 ) {

        // mark it as created in this session
        child->created_in_session = true;

        // we're creating, so this manifest is initialized (to zero blocks)
        child->manifest->initialize_empty( child->version );

        fs_entry_wlock( child );

        // call the driver
        err = driver_create_file( core, core->closure, path, child );

        if( err != 0 ) {
            // undo
            SG_error("driver_create_file(%s) rc = %d\n", path, err );

            child->open_count = 0;

            fs_entry_unlock( child );
            fs_entry_destroy( child, false );
            free( child );
        }

        else {

            // attach the file
            fs_entry_attach_lowlevel( core, parent, child );

            struct md_entry data;
            fs_entry_to_md_entry( core, &data, child, parent_id, parent_name );

            // create the child on the MS, obtaining its file ID and write nonce
            err = ms_client_create( core->ms, &child->file_id, &child->write_nonce, &data );

            md_entry_free( &data );

            if( err != 0 ) {
                SG_error( "ms_client_create(%s) rc = %d\n", path, err );
                err = -EREMOTEIO;

                child->open_count = 0;
                fs_entry_unlock( child );
                fs_entry_detach_lowlevel( core, parent, child );
                free( child );
            }
            else {
                fs_entry_unlock( child );
            }
        }
    }

    fs_entry_unlock( parent );

    free( parent_name );
    free( path_basename );

    return err;
}
Example #8
0
      struct fs_dir_entry* d_ent = NULL;

      // handle . and .. separately--we only want to lock children (not the current or parent directory)
      if( fent_name_hash == fs_entry_name_hash( "." ) ) {
         d_ent = SG_CALLOC( struct fs_dir_entry, 1 );
         d_ent->ftype = FTYPE_DIR;
         fs_entry_to_md_entry( core, &d_ent->data, dent, parent_id, parent_name );

         free( d_ent->data.name );
         d_ent->data.name = strdup(".");
      }
      else if( fent_name_hash == fs_entry_name_hash( ".." ) ) {
         d_ent = SG_CALLOC( struct fs_dir_entry, 1 );
         d_ent->ftype = FTYPE_DIR;
         char* parent_path = md_dirname( fs_path, NULL );

         if( fent != dent ) {
            fs_entry_to_md_entry( core, &d_ent->data, parent_path, SG_SYS_USER, dent->volume );
         }
         else {
            fs_entry_to_md_entry( core, &d_ent->data, dent, parent_id, parent_name );     // this is /
         }

         free( parent_path );

         free( d_ent->data.name );
         d_ent->data.name = strdup("..");
      }
      else {
         if( fent != dent && fs_entry_rlock( fent ) != 0 ) {