in src/meta/store/ops/BatchOperation.cc [557:605]
CoTryTask<bool> BatchedOp::openExists(IReadWriteTransaction &txn, Inode &inode, const CreateReq &req) {
CO_RETURN_ON_ERROR(req.valid());
if (inode.isSymlink()) {
// todo: rarely happens, how to handle this gracefully?
auto msg = fmt::format("req {}, found symlink {}", req, inode);
XLOG(WARN, msg);
co_return makeError(MetaCode::kBusy, std::move(msg));
}
if (!inode.isFile()) {
assert(inode.isDirectory());
co_return makeError(MetaCode::kIsDirectory);
}
if (req.flags.contains(O_EXCL)) {
co_return makeError(MetaCode::kExists);
}
// check permission
if (req.flags.accessType() != AccessType::READ && (inode.acl.iflags & FS_IMMUTABLE_FL)) {
co_return makeError(MetaCode::kNoPermission, fmt::format("FS_IMMUTABLE_FL set on inode {}", inode.id));
}
CO_RETURN_ON_ERROR(inode.acl.checkPermission(req.user, req.flags.accessType()));
// check hole
auto rdonly = req.flags.accessType() == AccessType::READ;
if (rdonly && inode.asFile().hasHole() && config().check_file_hole()) {
XLOGF(WARN, "Inode {} contains hole, don't allow O_RDONLY", inode.id);
co_return makeError(MetaCode::kFileHasHole);
}
auto dirty = false;
// clear SUID SGID sticky bits on write by non owner
constexpr uint32_t sbits = S_ISUID | S_ISGID | S_ISVTX;
static_assert(sbits == 07000);
if (!rdonly && req.user.uid != inode.acl.uid && (inode.acl.perm & sbits)) {
dirty |= SetAttr::update(inode.acl.perm, Permission(inode.acl.perm & (~sbits)));
}
// update dynamic stripe
if (req.session.has_value() && req.flags.accessType() != AccessType::READ) {
CO_RETURN_ON_ERROR(inode.acl.checkPermission(req.user, req.flags.accessType()));
if (!req.dynStripe && inode.asFile().dynStripe && inode.asFile().dynStripe < inode.asFile().layout.stripeSize) {
dirty |= SetAttr::update(inode.asFile().dynStripe, 0u);
}
}
// create session
if (req.session && req.flags.accessType() != AccessType::READ) {
openWrite.addSample(1);
CO_RETURN_ON_ERROR(co_await FileSession::create(inode.id, req.session.value()).store(txn));
}
co_return dirty;
}