@Override
  public void setMetadata(FileMetadata metadata, byte type, AtomicDBUpdate update)
      throws DatabaseException {

    assert (metadata instanceof BufferBackedFileMetadata);
    BufferBackedFileMetadata md = (BufferBackedFileMetadata) metadata;

    int index = md.getIndexId();
    if (type == -1)
      for (byte i = 0; i < BufferBackedFileMetadata.NUM_BUFFERS; i++) {
        byte[] valBuf = md.getValueBuffer(i);
        assert (valBuf != null);
        update.addUpdate(
            index,
            index == FILE_ID_INDEX
                ? BabuDBStorageHelper.createFileIdIndexKey(metadata.getId(), i)
                : md.getKeyBuffer(i),
            valBuf);
      }
    else {
      byte[] valBuf = md.getValueBuffer(type);
      assert (valBuf != null);
      update.addUpdate(
          index,
          index == FILE_ID_INDEX
              ? BabuDBStorageHelper.createFileIdIndexKey(metadata.getId(), type)
              : md.getKeyBuffer(type),
          valBuf);
    }
  }
  @Override
  public void startRequest(MRCRequest rq) throws Throwable {

    final removexattrRequest rqArgs = (removexattrRequest) rq.getRequestArgs();

    final VolumeManager vMan = master.getVolumeManager();
    final FileAccessManager faMan = master.getFileAccessManager();

    Path p = new Path(rqArgs.getVolumeName(), rqArgs.getPath());

    validateContext(rq);

    StorageManager sMan = vMan.getStorageManagerByName(p.getComp(0));
    PathResolver res = new PathResolver(sMan, p);

    // check whether the path prefix is searchable
    faMan.checkSearchPermission(
        sMan, res, rq.getDetails().userId, rq.getDetails().superUser, rq.getDetails().groupIds);

    // check whether file exists
    res.checkIfFileDoesNotExist();

    // retrieve and prepare the metadata to return
    FileMetadata file = res.getFile();

    AtomicDBUpdate update = sMan.createAtomicDBUpdate(master, rq);

    // if the attribute is a system attribute, set it

    final String attrKey = rqArgs.getName();

    // set a system attribute
    if (attrKey.startsWith("xtreemfs.")) {

      // check whether the user has privileged permissions to set
      // system attributes
      faMan.checkPrivilegedPermissions(
          sMan, file, rq.getDetails().userId, rq.getDetails().superUser, rq.getDetails().groupIds);

      MRCHelper.setSysAttrValue(
          sMan, vMan, faMan, res.getParentDirId(), file, attrKey.substring(9), "", update);
    }

    // set a user attribute
    else {

      sMan.setXAttr(file.getId(), rq.getDetails().userId, attrKey, null, update);
    }

    // update POSIX timestamps
    int time = (int) (TimeSync.getGlobalTime() / 1000);
    MRCHelper.updateFileTimes(res.getParentDirId(), file, false, true, false, sMan, time, update);

    // set the response
    rq.setResponse(timestampResponse.newBuilder().setTimestampS(time).build());

    update.execute();
  }
  protected static StatVFS getVolumeInfo(MRCRequestDispatcher master, StorageManager sMan)
      throws DatabaseException {

    final VolumeInfo volume = sMan.getVolumeInfo();
    final FileMetadata volumeRoot = sMan.getMetadata(1);

    int blockSize = sMan.getDefaultStripingPolicy(1).getStripeSize() * 1024;
    long bavail = master.getOSDStatusManager().getUsableSpace(volume.getId()) / blockSize;
    long bfree = master.getOSDStatusManager().getFreeSpace(volume.getId()) / blockSize;
    long blocks = master.getOSDStatusManager().getTotalSpace(volume.getId()) / blockSize;
    String volumeId = volume.getId();
    AccessControlPolicyType acPolId = AccessControlPolicyType.valueOf(volume.getAcPolicyId());
    StripingPolicy.Builder defaultStripingPolicy =
        Converter.stripingPolicyToStripingPolicy(sMan.getDefaultStripingPolicy(1));
    String volumeName = volume.getName();
    String owningGroupId = volumeRoot.getOwningGroupId();
    String ownerId = volumeRoot.getOwnerId();
    int perms = volumeRoot.getPerms();

    long newEtag = blockSize + bavail + blocks;

    return StatVFS.newBuilder()
        .setBsize(blockSize)
        .setBfree(bfree)
        .setBavail(bavail)
        .setBlocks(blocks)
        .setFsid(volumeId)
        .setNamemax(1024)
        .setOwnerUserId(ownerId)
        .setOwnerGroupId(owningGroupId)
        .setName(volumeName)
        .setEtag(newEtag)
        .setMode(perms)
        .setAccessControlPolicy(acPolId)
        .setDefaultStripingPolicy(defaultStripingPolicy)
        .build();
  }
  @Override
  public void link(
      final FileMetadata metadata,
      final long newParentId,
      final String newFileName,
      final AtomicDBUpdate update) {

    // get the link source
    BufferBackedFileMetadata md = (BufferBackedFileMetadata) metadata;

    // increment the link count
    short links = metadata.getLinkCount();
    md.setLinkCount((short) (links + 1));

    // insert the whole metadata of the original file in the file ID
    // index
    update.addUpdate(
        FILE_ID_INDEX,
        BabuDBStorageHelper.createFileIdIndexKey(metadata.getId(), FileMetadata.FC_METADATA),
        md.getFCMetadataValue());
    update.addUpdate(
        FILE_ID_INDEX,
        BabuDBStorageHelper.createFileIdIndexKey(metadata.getId(), FileMetadata.RC_METADATA),
        md.getRCMetadata().getValue());

    // remove the back link
    update.addUpdate(
        FILE_ID_INDEX, BabuDBStorageHelper.createFileIdIndexKey(metadata.getId(), (byte) 3), null);

    // if the metadata was retrieved from the FILE_INDEX and hasn't
    // been deleted before (i.e. links == 0), ensure that the original
    // file in the file index now points to the file ID index, and
    // remove the FC and XLoc metadata entries
    if (links != 0 && md.getIndexId() == FILE_INDEX) {

      update.addUpdate(
          FILE_INDEX,
          md.getRCMetadata().getKey(),
          BabuDBStorageHelper.createLinkTarget(metadata.getId()));
      update.addUpdate(FILE_INDEX, md.getFCMetadataKey(), null);
    }

    // create an entry for the new link to the metadata in the file
    // index
    update.addUpdate(
        FILE_INDEX,
        BabuDBStorageHelper.createFileKey(newParentId, newFileName, FileMetadata.RC_METADATA),
        BabuDBStorageHelper.createLinkTarget(metadata.getId()));
  }
  @Override
  public void createSnapshot(String snapName, long parentId, String dirName, boolean recursive)
      throws DatabaseException {

    try {

      // determine the prefixes for the snapshot
      byte[][][] prefixes = null;

      FileMetadata snapDir = getMetadata(parentId, dirName);

      // for a full volume snapshot, simply use a 'null' prefix (full:
      // dirID == 1 && recursive)
      if (snapDir.getId() != 1 || !recursive) {

        // get the IDs of all files and directories contained in the
        // given directory; if recursive == true, include subdirectories
        List<FileMetadata> nestedFiles = new LinkedList<FileMetadata>();
        BabuDBStorageHelper.getNestedFiles(nestedFiles, database, snapDir.getId(), recursive);

        List<byte[]> dirEntryPrefixes = new ArrayList<byte[]>(nestedFiles.size());
        List<byte[]> filePrefixes = new ArrayList<byte[]>(nestedFiles.size());

        // include the extended attributes of the volume's root
        // directory if it's not the snapshot directory - they are
        // needed to access volume-wide parameters in the snapshot, such
        // as the access control policy
        if (snapDir.getId() != 1) filePrefixes.add(ByteBuffer.wrap(new byte[8]).putLong(1).array());

        // include all metadata of the snapshot (i.e. top level) dir
        byte[] idxKey = BabuDBStorageHelper.createFileKey(parentId, dirName, (byte) -1);
        byte[] fileKey = BabuDBStorageHelper.createFilePrefixKey(snapDir.getId());
        dirEntryPrefixes.add(idxKey);
        filePrefixes.add(fileKey);

        // include the snapshot directory content
        idxKey = BabuDBStorageHelper.createFilePrefixKey(snapDir.getId());
        dirEntryPrefixes.add(idxKey);

        // determine the key prefixes of all nested files to include and
        // exclude
        for (FileMetadata file : nestedFiles) {

          // create a prefix key for the nested file
          byte[] key = BabuDBStorageHelper.createFilePrefixKey(file.getId());

          // if the nested file is a directory, ...
          if (file.isDirectory()) {

            // include the directory in the file prefixes
            // and the directory prefix in the dir entry prefixes
            filePrefixes.add(key);
            dirEntryPrefixes.add(key);
          }

          // if the nested file is a file, ...
          else filePrefixes.add(key);
        }

        byte[][] dirEntryPrefixesA = dirEntryPrefixes.toArray(new byte[dirEntryPrefixes.size()][]);
        byte[][] filePrefixesA = filePrefixes.toArray(new byte[filePrefixes.size()][]);

        Arrays.sort(dirEntryPrefixesA, DefaultByteRangeComparator.getInstance());
        Arrays.sort(filePrefixesA, DefaultByteRangeComparator.getInstance());

        // FILE_INDEX, XATTRS_INDEX, ACL_INDEX, FILE_ID_INDEX,
        // VOLUME_INDEX
        prefixes =
            new byte[][][] {dirEntryPrefixesA, filePrefixesA, filePrefixesA, filePrefixesA, null};
      }

      // create the snapshot
      SnapshotConfig snap = new DefaultSnapshotConfig(snapName, ALL_INDICES, prefixes, null);
      snapMan.createPersistentSnapshot(database.getName(), snap);

    } catch (BabuDBException exc) {
      throw new DatabaseException(exc);
    }
  }