static Result check()

in src/meta/store/ops/SetAttr.h [27:107]


  static Result<Void> check(const Inode &inode, const SetAttrReq &req, const Config &config) {
    RETURN_ON_ERROR(req.valid());

    // permission check for setPermission
    if (inode.id.isTreeRoot() && (req.perm || req.uid || req.gid)) {
      XLOGF(WARN, "Don't allow change permission of tree root {}!", inode.id);
      return makeError(MetaCode::kNoPermission, fmt::format("Don't allow change permission of {}", inode.id));
    }
    if (req.iflags.has_value() && *req.iflags != inode.acl.iflags) {
      auto setChainAllocation = !(inode.acl.iflags & FS_CHAIN_ALLOCATION_FL) && (*req.iflags & FS_CHAIN_ALLOCATION_FL);
      if (setChainAllocation && !config.iflags_chain_allocation()) {
        return makeError(MetaCode::kNoPermission, "FS_CHAIN_ALLOCATION_FL disabled");
      }
      auto setNewChunkEngine = !(inode.acl.iflags & FS_NEW_CHUNK_ENGINE) && (*req.iflags & FS_NEW_CHUNK_ENGINE);
      if (setNewChunkEngine && !config.iflags_chunk_engine()) {
        return makeError(MetaCode::kNoPermission, "FS_NEW_CHUNK_ENGINE disabled");
      }
      auto changed = *req.iflags ^ inode.acl.iflags;
      auto ownerChangeable = config.allow_owner_change_immutable() ? (uint32_t)(FS_HUGE_FILE_FL | FS_IMMUTABLE_FL)
                                                                   : (uint32_t)(FS_HUGE_FILE_FL);
      auto permCheck = req.user.isRoot() || (req.user.uid == inode.acl.uid && changed == (changed & ownerChangeable));
      if (!permCheck) {
        // NOTE: only allow root user set inode flags, file owner can use chattr +/- i, or set FS_HUGE_FILE_FL
        return makeError(MetaCode::kNoPermission, "only root can set iflags");
      }
    }
    if (req.perm.has_value() && *req.perm != inode.acl.perm && !req.user.isRoot() && req.user.uid != inode.acl.uid) {
      // man 2 chmod: The effective UID of the calling process must match the owner of the file, or the process must be
      // privileged (Linux: it must have the CAP_FOWNER capability).
      return makeError(MetaCode::kNoPermission, "no perm to set perm");
    }
    if (req.uid.has_value() && *req.uid != inode.acl.uid && !req.user.isRoot()) {
      // Only a privileged process (Linux: one with the CAP_CHOWN capability) may change the owner of a file.
      return makeError(MetaCode::kNoPermission, "no perm to set uid");
    }
    if (req.gid.has_value() && *req.gid != inode.acl.gid && !req.user.isRoot() &&
        (req.user.uid != inode.acl.uid || !req.user.inGroup(req.gid.value()))) {
      // The owner of a file may change the group of the file to any group of which that owner is a member.  A
      // privileged process (Linux: with CAP_CHOWN) may change the group arbitrarily.
      return makeError(MetaCode::kNoPermission, "no perm to set gid");
    }

    // permission check for utimes
    // To set both file timestamps to the current time (i.e., times is NULL, or both tv_nsec fields specify UTIME_NOW),
    // either:
    //   1. the caller must have write access to the file;
    //   2. the caller's effective user ID must match the owner of the file; or
    //   3. the caller must have appropriate privileges.
    // NOTE: we use UtcTime(0) as UTIME_NOW
    auto cond1 = inode.acl.checkPermission(req.user, AccessType::WRITE).hasValue();
    auto cond2 = req.user.uid == inode.acl.uid;
    auto cond3 = req.user.isRoot();
    if (req.atime || req.mtime) {
      if ((req.atime && req.atime != SETATTR_TIME_NOW) || (req.mtime && req.mtime != SETATTR_TIME_NOW)) {
        // To make any change other than setting both timestamps to the current time (i.e., times is not NULL, and
        // neither tv_nsec field is UTIME_NOW and neither tv_nsec field is UTIME_OMIT), either condition 2 or 3 above
        // must apply.
        if (!cond2 && !cond3) {
          return makeError(MetaCode::kNoPermission);
        }
      } else {
        if (!cond1 && !cond2 && !cond3) {
          return makeError(MetaCode::kNoPermission);
        }
      }
    }

    // permission check for setLayout
    if (req.layout) {
      if (!inode.isDirectory()) {
        return makeError(MetaCode::kNotDirectory, "setLayout but not directory");
      }
      RETURN_ON_ERROR(inode.acl.checkPermission(req.user, AccessType::WRITE));
    }

    if (!inode.isFile() && req.dynStripe) {
      return makeError(MetaCode::kNotFile, "extend dynStripe but not file");
    }

    return Void{};
  }