@Override
  public void setAttribute(String path, SetAttributePOptions options) throws IOException {
    if (options.hasPinned() || options.hasPersisted()
        || options.hasReplicationMax() || options.hasReplicationMin()
        || options.getXattrCount() != 0) {
      LOG.warn("UFS only supports setting mode, owner, and group. The other settings are "
              + "ignored (and no error is returned): {}",
          options);
    }

    UnderFileSystem ufs = getUfsInstance(path);
    if (options.hasMode()) {
      ufs.setMode(path, ModeUtils.protoToShort(options.getMode()));
    }
    if (options.hasOwner() && options.hasGroup()) {
      ufs.setOwner(path, options.getOwner(), options.getGroup());
    } else if (options.hasOwner()) {
      ufs.setOwner(path, options.getOwner(), null);
    } else if (options.hasGroup()) {
      ufs.setOwner(path, null, options.getGroup());
    }
    if (options.getXattrCount() > 0) {
      if (mXAttrWriteToUFSEnabled) {
        Map<String, ByteString> xattr = options.getXattrMap();
        for (Map.Entry<String, ByteString> attr : xattr.entrySet()) {
          ufs.setAttribute(path, attr.getKey(), attr.getValue().toByteArray());
        }
      }
    }
    mMetaManager.loadFromUfs(path);
    mMetaManager.invalidateListingCacheOfParent(path);
  }
@Test
  public void testSetAttribute() throws Exception {
    String fileContent = "foobar";
    File f = mTestFolder.newFile();
    Files.write(f.toPath(), fileContent.getBytes());

    mWorker.setAttribute(f.getPath(), SetAttributePOptions.newBuilder()
        .setMode(new Mode((short) 0444).toProto()).build());

    loadFileData(f.getPath());

    alluxio.wire.FileInfo result =
        mWorker.getFileInfo(f.getPath(), GetStatusPOptions.getDefaultInstance());
    List<PageId> cachedPages =
        mCacheManager.getCachedPageIdsByFileId(
            new AlluxioURI(f.getPath()).hash(), fileContent.length());
    assertEquals(1, cachedPages.size());
    assertEquals(0444, result.getMode());

    mWorker.setAttribute(f.getPath(), SetAttributePOptions.newBuilder()
        .setMode(new Mode((short) 0777).toProto()).build());
    result = mWorker.getFileInfo(f.getPath(), GetStatusPOptions.getDefaultInstance());
    cachedPages =
        mCacheManager.getCachedPageIdsByFileId(
            new AlluxioURI(f.getPath()).hash(), fileContent.length());
    // The data cache won't be invalidated after the MODE update,
    // as we know the content part does not change, by comparing the content hash.
    // Otherwise, the data cache will be invalidated, and we will get 0 page.
    assertEquals(1, cachedPages.size());
    assertEquals(0777, result.getMode());

    assertTrue(f.delete());
    // The target file is already deleted. setAttribute() should throw an exception.
    assertThrows(Exception.class, () ->
        mWorker.setAttribute(f.getPath(), SetAttributePOptions.newBuilder()
        .setMode(new Mode((short) 777).toProto()).build()));
  }