/*ARGSUSED7*/ static int xmem_create(struct vnode *dvp, char *nm, struct vattr *vap, enum vcexcl exclusive, int mode, struct vnode **vpp, struct cred *cred, int flag) { struct xmemnode *parent; struct xmount *xm; struct xmemnode *self; int error; struct xmemnode *oldxp; again: parent = (struct xmemnode *)VTOXN(dvp); xm = (struct xmount *)VTOXM(dvp); self = NULL; error = 0; oldxp = NULL; if (vap->va_type == VREG && (vap->va_mode & VSVTX)) { /* Must be privileged to set sticky bit */ if (secpolicy_vnode_stky_modify(cred) != 0) vap->va_mode &= ~VSVTX; } else if (vap->va_type == VNON) { return (EINVAL); } /* * Null component name is a synonym for directory being searched. */ if (*nm == '\0') { VN_HOLD(dvp); oldxp = parent; } else { error = xdirlookup(parent, nm, &oldxp, cred); } if (error == 0) { /* name found */ ASSERT(oldxp); rw_enter(&oldxp->xn_rwlock, RW_WRITER); /* * if create/read-only an existing * directory, allow it */ if (exclusive == EXCL) error = EEXIST; else if ((oldxp->xn_type == VDIR) && (mode & VWRITE)) error = EISDIR; else { error = xmem_xaccess(oldxp, mode, cred); } if (error) { rw_exit(&oldxp->xn_rwlock); xmemnode_rele(oldxp); return (error); } *vpp = XNTOV(oldxp); if ((*vpp)->v_type == VREG && (vap->va_mask & AT_SIZE) && vap->va_size == 0) { rw_enter(&oldxp->xn_contents, RW_WRITER); (void) xmemnode_trunc(xm, oldxp, 0); rw_exit(&oldxp->xn_contents); } rw_exit(&oldxp->xn_rwlock); if (IS_DEVVP(*vpp)) { struct vnode *newvp; newvp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cred); VN_RELE(*vpp); *vpp = newvp; } return (0); } if (error != ENOENT) return (error); rw_enter(&parent->xn_rwlock, RW_WRITER); error = xdirenter(xm, parent, nm, DE_CREATE, (struct xmemnode *)NULL, (struct xmemnode *)NULL, vap, &self, cred); rw_exit(&parent->xn_rwlock); if (error) { if (self) xmemnode_rele(self); if (error == EEXIST) { /* * This means that the file was created sometime * after we checked and did not find it and when * we went to create it. * Since creat() is supposed to truncate a file * that already exits go back to the begining * of the function. This time we will find it * and go down the xmem_trunc() path */ goto again; } return (error); } *vpp = XNTOV(self); if (IS_DEVVP(*vpp)) { struct vnode *newvp; newvp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cred); VN_RELE(*vpp); *vpp = newvp; } return (0); }
/* ARGSUSED7 */ int /* ERRNO if error, 0 if successful. */ sam_create_ino( sam_node_t *pip, /* pointer to parent directory inode. */ char *cp, /* pointer to the component name to create. */ vattr_t *vap, /* vattr ptr for type & mode information. */ vcexcl_t ex, /* exclusive create flag. */ int mode, /* file mode information. */ vnode_t **vpp, /* pointer pointer to returned vnode. */ cred_t *credp, /* credentials pointer. */ int filemode) /* open file mode */ { int error = 0; sam_node_t *ip; struct sam_name name; /* If no entry, slot info is returned here */ int trans_size; int issync; int truncflag = 0; int terr = 0; #ifdef LQFS_TODO_LOCKFS struct ulockfs *ulp; #endif /* LQFS_TODO_LOCKFS */ /* * Cannot set sticky bit unless superuser. */ if ((vap->va_mode & VSVTX) && secpolicy_vnode_stky_modify(credp)) { vap->va_mode &= ~VSVTX; } lookup_name: #ifdef LQFS_TODO_LOCKFS error = qfs_lockfs_begin(pip->mp, &ulp, ULOCKFS_CREATE_MASK); if (error) { return (error); } if (ulp) { #endif /* LQFS_TODO_LOCKFS */ /* Start LQFS create transaction */ trans_size = (int)TOP_CREATE_SIZE(pip); TRANS_BEGIN_CSYNC(pip->mp, issync, TOP_CREATE, trans_size); #ifdef LQFS_TODO_LOCKFS } #endif /* LQFS_TODO_LOCKFS */ RW_LOCK_OS(&pip->data_rwl, RW_WRITER); name.operation = SAM_CREATE; if ((error = sam_lookup_name(pip, cp, &ip, &name, credp)) == ENOENT) { if (((error = sam_create_name(pip, cp, &ip, &name, vap, credp)) != 0) && IS_SAM_ENOSPC(error)) { RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER); /* * Temporarily end LQFS create transaction */ #ifdef LQFS_TODO_LOCKFS if (ulp) { #endif /* LQFS_TODO_LOCKFS */ TRANS_END_CSYNC(pip->mp, terr, issync, TOP_CREATE, trans_size); #ifdef LQFS_TODO_LOCKFS } #endif /* LQFS_TODO_LOCKFS */ error = sam_wait_space(pip, error); if (error == 0) { error = terr; } if (error) { return (error); } goto lookup_name; } RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER); } else if (error == 0) { /* If entry already exists. */ RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER); error = EEXIST; if (ex == NONEXCL) { /* If non-exclusive create */ if ((S_ISDIR(ip->di.mode) || S_ISATTRDIR(ip->di.mode)) && (mode & S_IWRITE)) { /* Cannot create over an existing dir. */ error = EISDIR; } else if (SAM_PRIVILEGE_INO(ip->di.version, ip->di.id.ino)) { /* Cannot create over privileged inodes */ error = EPERM; } else if (mode) { /* Check mode if set */ error = sam_access_ino(ip, mode, FALSE, credp); } else { error = 0; } if ((error == 0) && S_ISREG(ip->di.mode) && (vap->va_mask & AT_SIZE) && (vap->va_size == 0)) { /* * If logging, do the truncate after the * LQFS create transaction is logged. */ if (TRANS_ISTRANS(ip->mp)) { truncflag++; } else { RW_LOCK_OS(&ip->inode_rwl, RW_WRITER); error = sam_clear_file(ip, 0, STALE_ARCHIVE, credp); RW_UNLOCK_OS(&ip->inode_rwl, RW_WRITER); } if (error == 0) { VNEVENT_CREATE_OS(SAM_ITOV(ip), NULL); } } } /* * Cannot do the following as it caused a stale of * offline copies. */ #if 0 if ((error == 0) && ((mode & O_CREAT) == 0)) { TRANS_INODE(ip->mp, ip); sam_mark_ino(ip, SAM_UPDATED|SAM_CHANGED); } #endif if (error) { VN_RELE(SAM_ITOV(ip)); /* Decrement v_count if error */ } } else { RW_UNLOCK_OS(&pip->data_rwl, RW_WRITER); } #ifdef LQFS_TODO_LOCKFS if (ulp) { #endif /* LQFS_TODO_LOCKFS */ TRANS_END_CSYNC(pip->mp, terr, issync, TOP_CREATE, trans_size); /* * If we haven't had a more interesting failure * already, then anything that might've happened * here should be reported. */ if (error == 0) { error = terr; } #ifdef LQFS_TODO_LOCKFS } #endif /* LQFS_TODO_LOCKFS */ if (!error && truncflag) { (void) TRANS_ITRUNC(ip, (u_offset_t)0, STALE_ARCHIVE, credp); } #ifdef LQFS_TODO_LOCKFS if (ulp) { qfs_lockfs_end(ulp); } #endif /* LQFS_TODO_LOCKFS */ if (error == 0) { *vpp = SAM_ITOV(ip); TRACE(T_SAM_CREATE_RET, SAM_ITOV(pip), (sam_tr_t)* vpp, ip->di.id.ino, error); } return (error); }