Changeset View
Changeset View
Standalone View
Standalone View
sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
#include <sys/dirent.h> | #include <sys/dirent.h> | ||||
#include <sys/policy.h> | #include <sys/policy.h> | ||||
#include <sys/sunddi.h> | #include <sys/sunddi.h> | ||||
#include <sys/filio.h> | #include <sys/filio.h> | ||||
#include <sys/sid.h> | #include <sys/sid.h> | ||||
#include <sys/zfs_ctldir.h> | #include <sys/zfs_ctldir.h> | ||||
#include <sys/zfs_fuid.h> | #include <sys/zfs_fuid.h> | ||||
#include <sys/zfs_sa.h> | #include <sys/zfs_sa.h> | ||||
#include <sys/dnlc.h> | |||||
#include <sys/zfs_rlock.h> | #include <sys/zfs_rlock.h> | ||||
#include <sys/extdirent.h> | #include <sys/extdirent.h> | ||||
#include <sys/kidmap.h> | #include <sys/kidmap.h> | ||||
#include <sys/bio.h> | #include <sys/bio.h> | ||||
#include <sys/buf.h> | #include <sys/buf.h> | ||||
#include <sys/sched.h> | #include <sys/sched.h> | ||||
#include <sys/acl.h> | #include <sys/acl.h> | ||||
#include <vm/vm_param.h> | #include <vm/vm_param.h> | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* (7) After dropping all locks, invoke zil_commit(zilog, foid) | * (7) After dropping all locks, invoke zil_commit(zilog, foid) | ||||
* to ensure that synchronous semantics are provided when necessary. | * to ensure that synchronous semantics are provided when necessary. | ||||
* | * | ||||
* In general, this is how things should be ordered in each vnode op: | * In general, this is how things should be ordered in each vnode op: | ||||
* | * | ||||
* ZFS_ENTER(zfsvfs); // exit if unmounted | * ZFS_ENTER(zfsvfs); // exit if unmounted | ||||
* top: | * top: | ||||
* zfs_dirent_lock(&dl, ...) // lock directory entry (may VN_HOLD()) | * zfs_dirent_lookup(&dl, ...) // lock directory entry (may VN_HOLD()) | ||||
* rw_enter(...); // grab any other locks you need | * rw_enter(...); // grab any other locks you need | ||||
* tx = dmu_tx_create(...); // get DMU tx | * tx = dmu_tx_create(...); // get DMU tx | ||||
* dmu_tx_hold_*(); // hold each object you might modify | * dmu_tx_hold_*(); // hold each object you might modify | ||||
* error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | * error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | ||||
* if (error) { | * if (error) { | ||||
* rw_exit(...); // drop locks | * rw_exit(...); // drop locks | ||||
* zfs_dirent_unlock(dl); // unlock directory entry | * zfs_dirent_unlock(dl); // unlock directory entry | ||||
* VN_RELE(...); // release held vnodes | * VN_RELE(...); // release held vnodes | ||||
▲ Show 20 Lines • Show All 1,270 Lines • ▼ Show 20 Lines | zfs_access(vnode_t *vp, int mode, int flag, cred_t *cr, | ||||
else | else | ||||
error = zfs_zaccess_rwx(zp, mode, flag, cr); | error = zfs_zaccess_rwx(zp, mode, flag, cr); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* If vnode is for a device return a specfs vnode instead. | |||||
*/ | |||||
static int | |||||
specvp_check(vnode_t **vpp, cred_t *cr) | |||||
{ | |||||
int error = 0; | |||||
if (IS_DEVVP(*vpp)) { | |||||
struct vnode *svp; | |||||
svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr); | |||||
VN_RELE(*vpp); | |||||
if (svp == NULL) | |||||
error = SET_ERROR(ENOSYS); | |||||
*vpp = svp; | |||||
} | |||||
return (error); | |||||
} | |||||
/* | |||||
* Lookup an entry in a directory, or an extended attribute directory. | * Lookup an entry in a directory, or an extended attribute directory. | ||||
* If it exists, return a held vnode reference for it. | * If it exists, return a held vnode reference for it. | ||||
* | * | ||||
* IN: dvp - vnode of directory to search. | * IN: dvp - vnode of directory to search. | ||||
* nm - name of entry to lookup. | * nm - name of entry to lookup. | ||||
* pnp - full pathname to lookup [UNUSED]. | * pnp - full pathname to lookup [UNUSED]. | ||||
* flags - LOOKUP_XATTR set if looking for an attribute. | * flags - LOOKUP_XATTR set if looking for an attribute. | ||||
* rdir - root directory vnode [UNUSED]. | * rdir - root directory vnode [UNUSED]. | ||||
* cr - credentials of caller. | * cr - credentials of caller. | ||||
* ct - caller context | * ct - caller context | ||||
* direntflags - directory lookup flags | |||||
* realpnp - returned pathname. | |||||
* | * | ||||
* OUT: vpp - vnode of located entry, NULL if not found. | * OUT: vpp - vnode of located entry, NULL if not found. | ||||
* | * | ||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* NA | * NA | ||||
*/ | */ | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
static int | static int | ||||
zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp, | zfs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct componentname *cnp, | ||||
int nameiop, cred_t *cr, kthread_t *td, int flags) | int nameiop, cred_t *cr, kthread_t *td, int flags) | ||||
{ | { | ||||
znode_t *zdp = VTOZ(dvp); | znode_t *zdp = VTOZ(dvp); | ||||
vnode_t *vp1; | |||||
zfsvfs_t *zfsvfs = zdp->z_zfsvfs; | zfsvfs_t *zfsvfs = zdp->z_zfsvfs; | ||||
int error = 0; | int error = 0; | ||||
int *direntflags = NULL; | |||||
void *realpnp = NULL; | |||||
/* fast path */ | /* fast path (should be redundant with vfs namecache) */ | ||||
if (!(flags & (LOOKUP_XATTR | FIGNORECASE))) { | if (!(flags & LOOKUP_XATTR)) { | ||||
if (dvp->v_type != VDIR) { | if (dvp->v_type != VDIR) { | ||||
return (SET_ERROR(ENOTDIR)); | return (SET_ERROR(ENOTDIR)); | ||||
} else if (zdp->z_sa_hdl == NULL) { | } else if (zdp->z_sa_hdl == NULL) { | ||||
return (SET_ERROR(EIO)); | return (SET_ERROR(EIO)); | ||||
} | } | ||||
if (nm[0] == 0 || (nm[0] == '.' && nm[1] == '\0')) { | |||||
error = zfs_fastaccesschk_execute(zdp, cr); | |||||
if (!error) { | |||||
*vpp = dvp; | |||||
VN_HOLD(*vpp); | |||||
return (0); | |||||
} | } | ||||
return (error); | |||||
} else { | |||||
vnode_t *tvp = dnlc_lookup(dvp, nm); | |||||
if (tvp) { | |||||
error = zfs_fastaccesschk_execute(zdp, cr); | |||||
if (error) { | |||||
VN_RELE(tvp); | |||||
return (error); | |||||
} | |||||
if (tvp == DNLC_NO_VNODE) { | |||||
VN_RELE(tvp); | |||||
return (SET_ERROR(ENOENT)); | |||||
} else { | |||||
*vpp = tvp; | |||||
return (specvp_check(vpp, cr)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
DTRACE_PROBE2(zfs__fastpath__lookup__miss, vnode_t *, dvp, char *, nm); | DTRACE_PROBE2(zfs__fastpath__lookup__miss, vnode_t *, dvp, char *, nm); | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(zdp); | ZFS_VERIFY_ZP(zdp); | ||||
*vpp = NULL; | *vpp = NULL; | ||||
if (flags & LOOKUP_XATTR) { | if (flags & LOOKUP_XATTR) { | ||||
Show All 19 Lines | #endif | ||||
if (error = zfs_get_xattrdir(VTOZ(dvp), vpp, cr, flags)) { | if (error = zfs_get_xattrdir(VTOZ(dvp), vpp, cr, flags)) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Do we have permission to get into attribute directory? | * Do we have permission to get into attribute directory? | ||||
*/ | */ | ||||
if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0, | if (error = zfs_zaccess(VTOZ(*vpp), ACE_EXECUTE, 0, | ||||
B_FALSE, cr)) { | B_FALSE, cr)) { | ||||
VN_RELE(*vpp); | vrele(*vpp); | ||||
*vpp = NULL; | *vpp = NULL; | ||||
} | } | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (dvp->v_type != VDIR) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(ENOTDIR)); | |||||
} | |||||
/* | /* | ||||
* Check accessibility of directory. | * Check accessibility of directory. | ||||
*/ | */ | ||||
if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) { | if (error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr)) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm), | if (zfsvfs->z_utf8 && u8_validate(nm, strlen(nm), | ||||
NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EILSEQ)); | return (SET_ERROR(EILSEQ)); | ||||
} | } | ||||
error = zfs_dirlook(zdp, nm, vpp, flags, direntflags, realpnp); | error = zfs_dirlook(dvp, nm, vpp, cnp->cn_lkflags); | ||||
if (error == 0) | if (error != 0) | ||||
error = specvp_check(vpp, cr); | *vpp = NULL; | ||||
ZFS_EXIT(zfsvfs); | |||||
/* Translate errors and add SAVENAME when needed. */ | /* Translate errors and add SAVENAME when needed. */ | ||||
if (cnp->cn_flags & ISLASTCN) { | if (cnp->cn_flags & ISLASTCN) { | ||||
switch (nameiop) { | switch (nameiop) { | ||||
case CREATE: | case CREATE: | ||||
case RENAME: | case RENAME: | ||||
if (error == ENOENT) { | if (error == ENOENT) { | ||||
error = EJUSTRETURN; | error = EJUSTRETURN; | ||||
cnp->cn_flags |= SAVENAME; | cnp->cn_flags |= SAVENAME; | ||||
break; | break; | ||||
} | } | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case DELETE: | case DELETE: | ||||
if (error == 0) | if (error == 0) | ||||
cnp->cn_flags |= SAVENAME; | cnp->cn_flags |= SAVENAME; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (error == 0 && (nm[0] != '.' || nm[1] != '\0')) { | |||||
int ltype = 0; | |||||
if (cnp->cn_flags & ISDOTDOT) { | /* Insert name into cache (as non-existent) if appropriate. */ | ||||
ltype = VOP_ISLOCKED(dvp); | |||||
VOP_UNLOCK(dvp, 0); | |||||
} | |||||
ZFS_EXIT(zfsvfs); | |||||
error = vn_lock(*vpp, cnp->cn_lkflags); | |||||
if (cnp->cn_flags & ISDOTDOT) | |||||
vn_lock(dvp, ltype | LK_RETRY); | |||||
if (error != 0) { | |||||
VN_RELE(*vpp); | |||||
*vpp = NULL; | |||||
return (error); | |||||
} | |||||
} else { | |||||
ZFS_EXIT(zfsvfs); | |||||
} | |||||
#ifdef FREEBSD_NAMECACHE | |||||
/* | |||||
* Insert name into cache (as non-existent) if appropriate. | |||||
*/ | |||||
if (error == ENOENT && (cnp->cn_flags & MAKEENTRY) != 0) | if (error == ENOENT && (cnp->cn_flags & MAKEENTRY) != 0) | ||||
cache_enter(dvp, *vpp, cnp); | cache_enter(dvp, NULL, cnp); | ||||
/* | |||||
* Insert name into cache if appropriate. | /* Insert name into cache if appropriate. */ | ||||
*/ | |||||
if (error == 0 && (cnp->cn_flags & MAKEENTRY)) { | if (error == 0 && (cnp->cn_flags & MAKEENTRY)) { | ||||
if (!(cnp->cn_flags & ISLASTCN) || | if (!(cnp->cn_flags & ISLASTCN) || | ||||
(nameiop != DELETE && nameiop != RENAME)) { | (nameiop != DELETE && nameiop != RENAME)) { | ||||
cache_enter(dvp, *vpp, cnp); | cache_enter(dvp, *vpp, cnp); | ||||
} | } | ||||
} | } | ||||
#endif | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Attempt to create a new entry in a directory. If the entry | * Attempt to create a new entry in a directory. If the entry | ||||
* already exists, truncate the file if permissible, else return | * already exists, truncate the file if permissible, else return | ||||
* an error. Return the vp of the created or trunc'd file. | * an error. Return the vp of the created or trunc'd file. | ||||
Show All 21 Lines | |||||
static int | static int | ||||
zfs_create(vnode_t *dvp, char *name, vattr_t *vap, int excl, int mode, | zfs_create(vnode_t *dvp, char *name, vattr_t *vap, int excl, int mode, | ||||
vnode_t **vpp, cred_t *cr, kthread_t *td) | vnode_t **vpp, cred_t *cr, kthread_t *td) | ||||
{ | { | ||||
znode_t *zp, *dzp = VTOZ(dvp); | znode_t *zp, *dzp = VTOZ(dvp); | ||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
objset_t *os; | objset_t *os; | ||||
zfs_dirlock_t *dl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
int error; | int error; | ||||
ksid_t *ksid; | ksid_t *ksid; | ||||
uid_t uid; | uid_t uid; | ||||
gid_t gid = crgetgid(cr); | gid_t gid = crgetgid(cr); | ||||
zfs_acl_ids_t acl_ids; | zfs_acl_ids_t acl_ids; | ||||
boolean_t fuid_dirtied; | boolean_t fuid_dirtied; | ||||
boolean_t have_acl = B_FALSE; | |||||
boolean_t waited = B_FALSE; | |||||
void *vsecp = NULL; | void *vsecp = NULL; | ||||
int flag = 0; | int flag = 0; | ||||
uint64_t txtype; | |||||
/* | /* | ||||
* If we have an ephemeral id, ACL, or XVATTR then | * If we have an ephemeral id, ACL, or XVATTR then | ||||
* make sure file system is at proper version | * make sure file system is at proper version | ||||
*/ | */ | ||||
ksid = crgetsid(cr, KSID_OWNER); | ksid = crgetsid(cr, KSID_OWNER); | ||||
if (ksid) | if (ksid) | ||||
Show All 20 Lines | zfs_create(vnode_t *dvp, char *name, vattr_t *vap, int excl, int mode, | ||||
if (vap->va_mask & AT_XVATTR) { | if (vap->va_mask & AT_XVATTR) { | ||||
if ((error = secpolicy_xvattr(dvp, (xvattr_t *)vap, | if ((error = secpolicy_xvattr(dvp, (xvattr_t *)vap, | ||||
crgetuid(cr), cr, vap->va_type)) != 0) { | crgetuid(cr), cr, vap->va_type)) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
getnewvnode_reserve(1); | |||||
top: | |||||
*vpp = NULL; | *vpp = NULL; | ||||
if ((vap->va_mode & S_ISVTX) && secpolicy_vnode_stky_modify(cr)) | if ((vap->va_mode & S_ISVTX) && secpolicy_vnode_stky_modify(cr)) | ||||
vap->va_mode &= ~S_ISVTX; | vap->va_mode &= ~S_ISVTX; | ||||
if (*name == '\0') { | error = zfs_dirent_lookup(dzp, name, &zp, ZNEW); | ||||
/* | |||||
* Null component name refers to the directory itself. | |||||
*/ | |||||
VN_HOLD(dvp); | |||||
zp = dzp; | |||||
dl = NULL; | |||||
error = 0; | |||||
} else { | |||||
/* possible VN_HOLD(zp) */ | |||||
int zflg = 0; | |||||
if (flag & FIGNORECASE) | |||||
zflg |= ZCILOOK; | |||||
error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, | |||||
NULL, NULL); | |||||
if (error) { | if (error) { | ||||
if (have_acl) | |||||
zfs_acl_ids_free(&acl_ids); | |||||
if (strcmp(name, "..") == 0) | |||||
error = SET_ERROR(EISDIR); | |||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | ASSERT3P(zp, ==, NULL); | ||||
if (zp == NULL) { | |||||
uint64_t txtype; | |||||
/* | /* | ||||
* Create a new file object and update the directory | * Create a new file object and update the directory | ||||
* to reference it. | * to reference it. | ||||
*/ | */ | ||||
if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | ||||
if (have_acl) | |||||
zfs_acl_ids_free(&acl_ids); | |||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* We only support the creation of regular files in | * We only support the creation of regular files in | ||||
* extended attribute directories. | * extended attribute directories. | ||||
*/ | */ | ||||
if ((dzp->z_pflags & ZFS_XATTR) && | if ((dzp->z_pflags & ZFS_XATTR) && | ||||
(vap->va_type != VREG)) { | (vap->va_type != VREG)) { | ||||
if (have_acl) | |||||
zfs_acl_ids_free(&acl_ids); | |||||
error = SET_ERROR(EINVAL); | error = SET_ERROR(EINVAL); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (!have_acl && (error = zfs_acl_ids_create(dzp, 0, vap, | if ((error = zfs_acl_ids_create(dzp, 0, vap, | ||||
cr, vsecp, &acl_ids)) != 0) | cr, vsecp, &acl_ids)) != 0) | ||||
goto out; | goto out; | ||||
have_acl = B_TRUE; | |||||
if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
error = SET_ERROR(EDQUOT); | error = SET_ERROR(EDQUOT); | ||||
goto out; | goto out; | ||||
} | } | ||||
getnewvnode_reserve(1); | |||||
tx = dmu_tx_create(os); | tx = dmu_tx_create(os); | ||||
dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | ||||
ZFS_SA_BASE_ATTR_SIZE); | ZFS_SA_BASE_ATTR_SIZE); | ||||
fuid_dirtied = zfsvfs->z_fuid_dirty; | fuid_dirtied = zfsvfs->z_fuid_dirty; | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_txhold(zfsvfs, tx); | zfs_fuid_txhold(zfsvfs, tx); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | ||||
dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); | ||||
if (!zfsvfs->z_use_sa && | if (!zfsvfs->z_use_sa && | ||||
acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | ||||
0, acl_ids.z_aclp->z_acl_bytes); | 0, acl_ids.z_aclp->z_acl_bytes); | ||||
} | } | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
zfs_dirent_unlock(dl); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | |||||
goto top; | |||||
} | |||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_sync(zfsvfs, tx); | zfs_fuid_sync(zfsvfs, tx); | ||||
(void) zfs_link_create(dl, zp, tx, ZNEW); | (void) zfs_link_create(dzp, name, zp, tx, ZNEW); | ||||
txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap); | txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap); | ||||
if (flag & FIGNORECASE) | |||||
txtype |= TX_CI; | |||||
zfs_log_create(zilog, tx, txtype, dzp, zp, name, | zfs_log_create(zilog, tx, txtype, dzp, zp, name, | ||||
vsecp, acl_ids.z_fuidp, vap); | vsecp, acl_ids.z_fuidp, vap); | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
} else { | |||||
int aflags = (flag & FAPPEND) ? V_APPEND : 0; | |||||
if (have_acl) | |||||
zfs_acl_ids_free(&acl_ids); | |||||
have_acl = B_FALSE; | |||||
/* | |||||
* A directory entry already exists for this name. | |||||
*/ | |||||
/* | |||||
* Can't truncate an existing file if in exclusive mode. | |||||
*/ | |||||
if (excl == EXCL) { | |||||
error = SET_ERROR(EEXIST); | |||||
goto out; | |||||
} | |||||
/* | |||||
* Can't open a directory for writing. | |||||
*/ | |||||
if ((ZTOV(zp)->v_type == VDIR) && (mode & S_IWRITE)) { | |||||
error = SET_ERROR(EISDIR); | |||||
goto out; | |||||
} | |||||
/* | |||||
* Verify requested access to file. | |||||
*/ | |||||
if (mode && (error = zfs_zaccess_rwx(zp, mode, aflags, cr))) { | |||||
goto out; | |||||
} | |||||
mutex_enter(&dzp->z_lock); | |||||
dzp->z_seq++; | |||||
mutex_exit(&dzp->z_lock); | |||||
/* | |||||
* Truncate regular files if requested. | |||||
*/ | |||||
if ((ZTOV(zp)->v_type == VREG) && | |||||
(vap->va_mask & AT_SIZE) && (vap->va_size == 0)) { | |||||
/* we can't hold any locks when calling zfs_freesp() */ | |||||
zfs_dirent_unlock(dl); | |||||
dl = NULL; | |||||
error = zfs_freesp(zp, 0, 0, mode, TRUE); | |||||
if (error == 0) { | |||||
vnevent_create(ZTOV(zp), ct); | |||||
} | |||||
} | |||||
} | |||||
out: | |||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
if (dl) | |||||
zfs_dirent_unlock(dl); | |||||
if (error) { | out: | ||||
if (zp) | if (error == 0) { | ||||
VN_RELE(ZTOV(zp)); | |||||
} else { | |||||
*vpp = ZTOV(zp); | *vpp = ZTOV(zp); | ||||
error = specvp_check(vpp, cr); | |||||
} | } | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 9 Lines | |||||
* | * | ||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* dvp - ctime|mtime | * dvp - ctime|mtime | ||||
* vp - ctime (if nlink > 0) | * vp - ctime (if nlink > 0) | ||||
*/ | */ | ||||
uint64_t null_xattr = 0; | |||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct, | zfs_remove(vnode_t *dvp, vnode_t *vp, char *name, cred_t *cr) | ||||
int flags) | |||||
{ | { | ||||
znode_t *zp, *dzp = VTOZ(dvp); | znode_t *zp, *dzp = VTOZ(dvp); | ||||
znode_t *xzp; | znode_t *xzp; | ||||
vnode_t *vp; | |||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
uint64_t acl_obj, xattr_obj; | uint64_t acl_obj, xattr_obj; | ||||
uint64_t xattr_obj_unlinked = 0; | |||||
uint64_t obj = 0; | uint64_t obj = 0; | ||||
zfs_dirlock_t *dl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
boolean_t may_delete_now, delete_now = FALSE; | |||||
boolean_t unlinked, toobig = FALSE; | boolean_t unlinked, toobig = FALSE; | ||||
uint64_t txtype; | uint64_t txtype; | ||||
pathname_t *realnmp = NULL; | |||||
pathname_t realnm; | |||||
int error; | int error; | ||||
int zflg = ZEXISTS; | |||||
boolean_t waited = B_FALSE; | |||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(dzp); | ZFS_VERIFY_ZP(dzp); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
zp = VTOZ(vp); | |||||
if (flags & FIGNORECASE) { | |||||
zflg |= ZCILOOK; | |||||
pn_alloc(&realnm); | |||||
realnmp = &realnm; | |||||
} | |||||
top: | |||||
xattr_obj = 0; | xattr_obj = 0; | ||||
xzp = NULL; | xzp = NULL; | ||||
/* | |||||
* Attempt to lock directory; fail if entry doesn't exist. | |||||
*/ | |||||
if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, | |||||
NULL, realnmp)) { | |||||
if (realnmp) | |||||
pn_free(realnmp); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
vp = ZTOV(zp); | |||||
if (error = zfs_zaccess_delete(dzp, zp, cr)) { | if (error = zfs_zaccess_delete(dzp, zp, cr)) { | ||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* Need to use rmdir for removing directories. | * Need to use rmdir for removing directories. | ||||
*/ | */ | ||||
if (vp->v_type == VDIR) { | if (vp->v_type == VDIR) { | ||||
error = SET_ERROR(EPERM); | error = SET_ERROR(EPERM); | ||||
goto out; | goto out; | ||||
} | } | ||||
vnevent_remove(vp, dvp, name, ct); | vnevent_remove(vp, dvp, name, ct); | ||||
if (realnmp) | obj = zp->z_id; | ||||
dnlc_remove(dvp, realnmp->pn_buf); | |||||
else | |||||
dnlc_remove(dvp, name); | |||||
VI_LOCK(vp); | /* are there any extended attributes? */ | ||||
may_delete_now = vp->v_count == 1 && !vn_has_cached_data(vp); | error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), | ||||
VI_UNLOCK(vp); | &xattr_obj, sizeof (xattr_obj)); | ||||
if (error == 0 && xattr_obj) { | |||||
error = zfs_zget(zfsvfs, xattr_obj, &xzp); | |||||
ASSERT0(error); | |||||
} | |||||
/* | /* | ||||
* We may delete the znode now, or we may put it in the unlinked set; | * We may delete the znode now, or we may put it in the unlinked set; | ||||
* it depends on whether we're the last link, and on whether there are | * it depends on whether we're the last link, and on whether there are | ||||
* other holds on the vnode. So we dmu_tx_hold() the right things to | * other holds on the vnode. So we dmu_tx_hold() the right things to | ||||
* allow for either case. | * allow for either case. | ||||
*/ | */ | ||||
obj = zp->z_id; | |||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); | dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); | ||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | ||||
zfs_sa_upgrade_txholds(tx, zp); | zfs_sa_upgrade_txholds(tx, zp); | ||||
zfs_sa_upgrade_txholds(tx, dzp); | zfs_sa_upgrade_txholds(tx, dzp); | ||||
if (may_delete_now) { | |||||
toobig = | |||||
zp->z_size > zp->z_blksz * DMU_MAX_DELETEBLKCNT; | |||||
/* if the file is too big, only hold_free a token amount */ | |||||
dmu_tx_hold_free(tx, zp->z_id, 0, | |||||
(toobig ? DMU_MAX_ACCESS : DMU_OBJECT_END)); | |||||
} | |||||
/* are there any extended attributes? */ | if (xzp) { | ||||
error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), | |||||
&xattr_obj, sizeof (xattr_obj)); | |||||
if (error == 0 && xattr_obj) { | |||||
error = zfs_zget(zfsvfs, xattr_obj, &xzp); | |||||
ASSERT0(error); | |||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | ||||
dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); | ||||
} | } | ||||
mutex_enter(&zp->z_lock); | |||||
if ((acl_obj = zfs_external_acl(zp)) != 0 && may_delete_now) | |||||
dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); | |||||
mutex_exit(&zp->z_lock); | |||||
/* charge as an update -- would be nice not to charge at all */ | /* charge as an update -- would be nice not to charge at all */ | ||||
dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | ||||
/* | /* | ||||
* Mark this transaction as typically resulting in a net free of space | * Mark this transaction as typically resulting in a net free of space | ||||
*/ | */ | ||||
dmu_tx_mark_netfree(tx); | dmu_tx_mark_netfree(tx); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
zfs_dirent_unlock(dl); | |||||
VN_RELE(vp); | |||||
if (xzp) | |||||
VN_RELE(ZTOV(xzp)); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
goto top; | |||||
} | |||||
if (realnmp) | |||||
pn_free(realnmp); | |||||
dmu_tx_abort(tx); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Remove the directory entry. | * Remove the directory entry. | ||||
*/ | */ | ||||
error = zfs_link_destroy(dl, zp, tx, zflg, &unlinked); | error = zfs_link_destroy(dzp, name, zp, tx, ZEXISTS, &unlinked); | ||||
if (error) { | if (error) { | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (unlinked) { | if (unlinked) { | ||||
/* | |||||
* Hold z_lock so that we can make sure that the ACL obj | |||||
* hasn't changed. Could have been deleted due to | |||||
* zfs_sa_upgrade(). | |||||
*/ | |||||
mutex_enter(&zp->z_lock); | |||||
VI_LOCK(vp); | |||||
(void) sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), | |||||
&xattr_obj_unlinked, sizeof (xattr_obj_unlinked)); | |||||
delete_now = may_delete_now && !toobig && | |||||
vp->v_count == 1 && !vn_has_cached_data(vp) && | |||||
xattr_obj == xattr_obj_unlinked && zfs_external_acl(zp) == | |||||
acl_obj; | |||||
VI_UNLOCK(vp); | |||||
} | |||||
if (delete_now) { | |||||
#ifdef __FreeBSD__ | |||||
panic("zfs_remove: delete_now branch taken"); | |||||
#endif | |||||
if (xattr_obj_unlinked) { | |||||
ASSERT3U(xzp->z_links, ==, 2); | |||||
mutex_enter(&xzp->z_lock); | |||||
xzp->z_unlinked = 1; | |||||
xzp->z_links = 0; | |||||
error = sa_update(xzp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs), | |||||
&xzp->z_links, sizeof (xzp->z_links), tx); | |||||
ASSERT3U(error, ==, 0); | |||||
mutex_exit(&xzp->z_lock); | |||||
zfs_unlinked_add(xzp, tx); | |||||
if (zp->z_is_sa) | |||||
error = sa_remove(zp->z_sa_hdl, | |||||
SA_ZPL_XATTR(zfsvfs), tx); | |||||
else | |||||
error = sa_update(zp->z_sa_hdl, | |||||
SA_ZPL_XATTR(zfsvfs), &null_xattr, | |||||
sizeof (uint64_t), tx); | |||||
ASSERT0(error); | |||||
} | |||||
VI_LOCK(vp); | |||||
vp->v_count--; | |||||
ASSERT0(vp->v_count); | |||||
VI_UNLOCK(vp); | |||||
mutex_exit(&zp->z_lock); | |||||
zfs_znode_delete(zp, tx); | |||||
} else if (unlinked) { | |||||
mutex_exit(&zp->z_lock); | |||||
zfs_unlinked_add(zp, tx); | zfs_unlinked_add(zp, tx); | ||||
#ifdef __FreeBSD__ | |||||
vp->v_vflag |= VV_NOSYNC; | vp->v_vflag |= VV_NOSYNC; | ||||
#endif | |||||
} | } | ||||
txtype = TX_REMOVE; | txtype = TX_REMOVE; | ||||
if (flags & FIGNORECASE) | |||||
txtype |= TX_CI; | |||||
zfs_log_remove(zilog, tx, txtype, dzp, name, obj); | zfs_log_remove(zilog, tx, txtype, dzp, name, obj); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
out: | out: | ||||
if (realnmp) | |||||
pn_free(realnmp); | |||||
zfs_dirent_unlock(dl); | |||||
if (!delete_now) | |||||
VN_RELE(vp); | |||||
if (xzp) | if (xzp) | ||||
VN_RELE(ZTOV(xzp)); | vrele(ZTOV(xzp)); | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 14 Lines | |||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* dvp - ctime|mtime updated | * dvp - ctime|mtime updated | ||||
* vp - ctime|mtime|atime updated | * vp - ctime|mtime|atime updated | ||||
*/ | */ | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr, | zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr) | ||||
caller_context_t *ct, int flags, vsecattr_t *vsecp) | |||||
{ | { | ||||
znode_t *zp, *dzp = VTOZ(dvp); | znode_t *zp, *dzp = VTOZ(dvp); | ||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
zfs_dirlock_t *dl; | |||||
uint64_t txtype; | uint64_t txtype; | ||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
int error; | int error; | ||||
int zf = ZNEW; | |||||
ksid_t *ksid; | ksid_t *ksid; | ||||
uid_t uid; | uid_t uid; | ||||
gid_t gid = crgetgid(cr); | gid_t gid = crgetgid(cr); | ||||
zfs_acl_ids_t acl_ids; | zfs_acl_ids_t acl_ids; | ||||
boolean_t fuid_dirtied; | boolean_t fuid_dirtied; | ||||
boolean_t waited = B_FALSE; | |||||
ASSERT(vap->va_type == VDIR); | ASSERT(vap->va_type == VDIR); | ||||
/* | /* | ||||
* If we have an ephemeral id, ACL, or XVATTR then | * If we have an ephemeral id, ACL, or XVATTR then | ||||
* make sure file system is at proper version | * make sure file system is at proper version | ||||
*/ | */ | ||||
ksid = crgetsid(cr, KSID_OWNER); | ksid = crgetsid(cr, KSID_OWNER); | ||||
if (ksid) | if (ksid) | ||||
uid = ksid_getid(ksid); | uid = ksid_getid(ksid); | ||||
else | else | ||||
uid = crgetuid(cr); | uid = crgetuid(cr); | ||||
if (zfsvfs->z_use_fuids == B_FALSE && | if (zfsvfs->z_use_fuids == B_FALSE && | ||||
(vsecp || (vap->va_mask & AT_XVATTR) || | ((vap->va_mask & AT_XVATTR) || | ||||
IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid))) | IS_EPHEMERAL(uid) || IS_EPHEMERAL(gid))) | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(dzp); | ZFS_VERIFY_ZP(dzp); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
if (dzp->z_pflags & ZFS_XATTR) { | if (dzp->z_pflags & ZFS_XATTR) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
} | } | ||||
if (zfsvfs->z_utf8 && u8_validate(dirname, | if (zfsvfs->z_utf8 && u8_validate(dirname, | ||||
strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | strlen(dirname), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EILSEQ)); | return (SET_ERROR(EILSEQ)); | ||||
} | } | ||||
if (flags & FIGNORECASE) | |||||
zf |= ZCILOOK; | |||||
if (vap->va_mask & AT_XVATTR) { | if (vap->va_mask & AT_XVATTR) { | ||||
if ((error = secpolicy_xvattr(dvp, (xvattr_t *)vap, | if ((error = secpolicy_xvattr(dvp, (xvattr_t *)vap, | ||||
crgetuid(cr), cr, vap->va_type)) != 0) { | crgetuid(cr), cr, vap->va_type)) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
if ((error = zfs_acl_ids_create(dzp, 0, vap, cr, | if ((error = zfs_acl_ids_create(dzp, 0, vap, cr, | ||||
vsecp, &acl_ids)) != 0) { | NULL, &acl_ids)) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
getnewvnode_reserve(1); | |||||
/* | /* | ||||
* First make sure the new directory doesn't exist. | * First make sure the new directory doesn't exist. | ||||
* | * | ||||
* Existence is checked first to make sure we don't return | * Existence is checked first to make sure we don't return | ||||
* EACCES instead of EEXIST which can cause some applications | * EACCES instead of EEXIST which can cause some applications | ||||
* to fail. | * to fail. | ||||
*/ | */ | ||||
top: | |||||
*vpp = NULL; | *vpp = NULL; | ||||
if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf, | if (error = zfs_dirent_lookup(dzp, dirname, &zp, ZNEW)) { | ||||
NULL, NULL)) { | |||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
ASSERT3P(zp, ==, NULL); | |||||
if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) { | if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
zfs_dirent_unlock(dl); | |||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
zfs_dirent_unlock(dl); | |||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EDQUOT)); | return (SET_ERROR(EDQUOT)); | ||||
} | } | ||||
/* | /* | ||||
* Add a new entry to the directory. | * Add a new entry to the directory. | ||||
*/ | */ | ||||
getnewvnode_reserve(1); | |||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname); | dmu_tx_hold_zap(tx, dzp->z_id, TRUE, dirname); | ||||
dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); | dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL); | ||||
fuid_dirtied = zfsvfs->z_fuid_dirty; | fuid_dirtied = zfsvfs->z_fuid_dirty; | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_txhold(zfsvfs, tx); | zfs_fuid_txhold(zfsvfs, tx); | ||||
if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, | ||||
acl_ids.z_aclp->z_acl_bytes); | acl_ids.z_aclp->z_acl_bytes); | ||||
} | } | ||||
dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | ||||
ZFS_SA_BASE_ATTR_SIZE); | ZFS_SA_BASE_ATTR_SIZE); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
zfs_dirent_unlock(dl); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | |||||
goto top; | |||||
} | |||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Create new node. | * Create new node. | ||||
*/ | */ | ||||
zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_sync(zfsvfs, tx); | zfs_fuid_sync(zfsvfs, tx); | ||||
/* | /* | ||||
* Now put new name in parent dir. | * Now put new name in parent dir. | ||||
*/ | */ | ||||
(void) zfs_link_create(dl, zp, tx, ZNEW); | (void) zfs_link_create(dzp, dirname, zp, tx, ZNEW); | ||||
*vpp = ZTOV(zp); | *vpp = ZTOV(zp); | ||||
txtype = zfs_log_create_txtype(Z_DIR, vsecp, vap); | txtype = zfs_log_create_txtype(Z_DIR, NULL, vap); | ||||
if (flags & FIGNORECASE) | zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, NULL, | ||||
txtype |= TX_CI; | |||||
zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, vsecp, | |||||
acl_ids.z_fuidp, vap); | acl_ids.z_fuidp, vap); | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
zfs_dirent_unlock(dl); | |||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
Show All 10 Lines | |||||
* | * | ||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* dvp - ctime|mtime updated | * dvp - ctime|mtime updated | ||||
*/ | */ | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_rmdir(vnode_t *dvp, char *name, vnode_t *cwd, cred_t *cr, | zfs_rmdir(vnode_t *dvp, vnode_t *vp, char *name, cred_t *cr) | ||||
caller_context_t *ct, int flags) | |||||
{ | { | ||||
znode_t *dzp = VTOZ(dvp); | znode_t *dzp = VTOZ(dvp); | ||||
znode_t *zp; | znode_t *zp; | ||||
vnode_t *vp; | |||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
zfs_dirlock_t *dl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
int error; | int error; | ||||
int zflg = ZEXISTS; | |||||
boolean_t waited = B_FALSE; | |||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(dzp); | ZFS_VERIFY_ZP(dzp); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
if (flags & FIGNORECASE) | zp = VTOZ(vp); | ||||
zflg |= ZCILOOK; | |||||
top: | |||||
zp = NULL; | |||||
/* | |||||
* Attempt to lock directory; fail if entry doesn't exist. | |||||
*/ | |||||
if (error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, | |||||
NULL, NULL)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
vp = ZTOV(zp); | |||||
if (error = zfs_zaccess_delete(dzp, zp, cr)) { | if (error = zfs_zaccess_delete(dzp, zp, cr)) { | ||||
goto out; | goto out; | ||||
} | } | ||||
if (vp->v_type != VDIR) { | if (vp->v_type != VDIR) { | ||||
error = SET_ERROR(ENOTDIR); | error = SET_ERROR(ENOTDIR); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (vp == cwd) { | |||||
error = SET_ERROR(EINVAL); | |||||
goto out; | |||||
} | |||||
vnevent_rmdir(vp, dvp, name, ct); | vnevent_rmdir(vp, dvp, name, ct); | ||||
/* | |||||
* Grab a lock on the directory to make sure that noone is | |||||
* trying to add (or lookup) entries while we are removing it. | |||||
*/ | |||||
rw_enter(&zp->z_name_lock, RW_WRITER); | |||||
/* | |||||
* Grab a lock on the parent pointer to make sure we play well | |||||
* with the treewalk and directory rename code. | |||||
*/ | |||||
rw_enter(&zp->z_parent_lock, RW_WRITER); | |||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); | dmu_tx_hold_zap(tx, dzp->z_id, FALSE, name); | ||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | ||||
dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | ||||
zfs_sa_upgrade_txholds(tx, zp); | zfs_sa_upgrade_txholds(tx, zp); | ||||
zfs_sa_upgrade_txholds(tx, dzp); | zfs_sa_upgrade_txholds(tx, dzp); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
rw_exit(&zp->z_parent_lock); | |||||
rw_exit(&zp->z_name_lock); | |||||
zfs_dirent_unlock(dl); | |||||
VN_RELE(vp); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
goto top; | |||||
} | |||||
dmu_tx_abort(tx); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef FREEBSD_NAMECACHE | |||||
cache_purge(dvp); | cache_purge(dvp); | ||||
#endif | |||||
error = zfs_link_destroy(dl, zp, tx, zflg, NULL); | error = zfs_link_destroy(dzp, name, zp, tx, ZEXISTS, NULL); | ||||
if (error == 0) { | if (error == 0) { | ||||
uint64_t txtype = TX_RMDIR; | uint64_t txtype = TX_RMDIR; | ||||
if (flags & FIGNORECASE) | |||||
txtype |= TX_CI; | |||||
zfs_log_remove(zilog, tx, txtype, dzp, name, ZFS_NO_OBJECT); | zfs_log_remove(zilog, tx, txtype, dzp, name, ZFS_NO_OBJECT); | ||||
} | } | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
rw_exit(&zp->z_parent_lock); | |||||
rw_exit(&zp->z_name_lock); | |||||
#ifdef FREEBSD_NAMECACHE | |||||
cache_purge(vp); | cache_purge(vp); | ||||
#endif | |||||
out: | out: | ||||
zfs_dirent_unlock(dl); | |||||
VN_RELE(vp); | |||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 208 Lines • ▼ Show 20 Lines | if (flags & V_RDDIR_ACCFILTER) { | ||||
/* | /* | ||||
* If we have no access at all, don't include | * If we have no access at all, don't include | ||||
* this entry in the returned information | * this entry in the returned information | ||||
*/ | */ | ||||
znode_t *ezp; | znode_t *ezp; | ||||
if (zfs_zget(zp->z_zfsvfs, objnum, &ezp) != 0) | if (zfs_zget(zp->z_zfsvfs, objnum, &ezp) != 0) | ||||
goto skip_entry; | goto skip_entry; | ||||
if (!zfs_has_access(ezp, cr)) { | if (!zfs_has_access(ezp, cr)) { | ||||
VN_RELE(ZTOV(ezp)); | vrele(ZTOV(ezp)); | ||||
goto skip_entry; | goto skip_entry; | ||||
} | } | ||||
VN_RELE(ZTOV(ezp)); | vrele(ZTOV(ezp)); | ||||
} | } | ||||
if (flags & V_RDDIR_ENTFLAGS) | if (flags & V_RDDIR_ENTFLAGS) | ||||
reclen = EDIRENT_RECLEN(strlen(zap.za_name)); | reclen = EDIRENT_RECLEN(strlen(zap.za_name)); | ||||
else | else | ||||
reclen = DIRENT64_RECLEN(strlen(zap.za_name)); | reclen = DIRENT64_RECLEN(strlen(zap.za_name)); | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | if (!(zp->z_pflags & ZFS_ACL_TRIVIAL) && | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Return all attributes. It's cheaper to provide the answer | * Return all attributes. It's cheaper to provide the answer | ||||
* than to determine whether we were asked the question. | * than to determine whether we were asked the question. | ||||
*/ | */ | ||||
mutex_enter(&zp->z_lock); | |||||
vap->va_type = IFTOVT(zp->z_mode); | vap->va_type = IFTOVT(zp->z_mode); | ||||
vap->va_mode = zp->z_mode & ~S_IFMT; | vap->va_mode = zp->z_mode & ~S_IFMT; | ||||
#ifdef illumos | #ifdef illumos | ||||
vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev; | vap->va_fsid = zp->z_zfsvfs->z_vfs->vfs_dev; | ||||
#else | #else | ||||
vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; | vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0]; | ||||
#endif | #endif | ||||
vap->va_nodeid = zp->z_id; | vap->va_nodeid = zp->z_id; | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | if ((xoap = xva_getxoptattr(xvap)) != NULL && zfsvfs->z_use_fuids) { | ||||
} | } | ||||
} | } | ||||
ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime); | ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime); | ||||
ZFS_TIME_DECODE(&vap->va_mtime, mtime); | ZFS_TIME_DECODE(&vap->va_mtime, mtime); | ||||
ZFS_TIME_DECODE(&vap->va_ctime, ctime); | ZFS_TIME_DECODE(&vap->va_ctime, ctime); | ||||
ZFS_TIME_DECODE(&vap->va_birthtime, crtime); | ZFS_TIME_DECODE(&vap->va_birthtime, crtime); | ||||
mutex_exit(&zp->z_lock); | |||||
sa_object_size(zp->z_sa_hdl, &blksize, &nblocks); | sa_object_size(zp->z_sa_hdl, &blksize, &nblocks); | ||||
vap->va_blksize = blksize; | vap->va_blksize = blksize; | ||||
vap->va_bytes = nblocks << 9; /* nblocks * 512 */ | vap->va_bytes = nblocks << 9; /* nblocks * 512 */ | ||||
if (zp->z_blksz == 0) { | if (zp->z_blksz == 0) { | ||||
/* | /* | ||||
* Block size hasn't been set; suggest maximal I/O transfers. | * Block size hasn't been set; suggest maximal I/O transfers. | ||||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, | ||||
if (mask & (AT_ATIME | AT_MTIME)) { | if (mask & (AT_ATIME | AT_MTIME)) { | ||||
if (((mask & AT_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) || | if (((mask & AT_ATIME) && TIMESPEC_OVERFLOW(&vap->va_atime)) || | ||||
((mask & AT_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) { | ((mask & AT_MTIME) && TIMESPEC_OVERFLOW(&vap->va_mtime))) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EOVERFLOW)); | return (SET_ERROR(EOVERFLOW)); | ||||
} | } | ||||
} | } | ||||
top: | |||||
attrzp = NULL; | attrzp = NULL; | ||||
aclp = NULL; | aclp = NULL; | ||||
/* Can this be moved to before the top label? */ | /* Can this be moved to before the top label? */ | ||||
if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { | if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EROFS)); | return (SET_ERROR(EROFS)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | if (((idmask == (AT_UID|AT_GID)) && take_owner && take_group) || | ||||
} else { | } else { | ||||
need_policy = TRUE; | need_policy = TRUE; | ||||
} | } | ||||
} else { | } else { | ||||
need_policy = TRUE; | need_policy = TRUE; | ||||
} | } | ||||
} | } | ||||
mutex_enter(&zp->z_lock); | |||||
oldva.va_mode = zp->z_mode; | oldva.va_mode = zp->z_mode; | ||||
zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid); | zfs_fuid_map_ids(zp, cr, &oldva.va_uid, &oldva.va_gid); | ||||
if (mask & AT_XVATTR) { | if (mask & AT_XVATTR) { | ||||
/* | /* | ||||
* Update xvattr mask to include only those attributes | * Update xvattr mask to include only those attributes | ||||
* that are actually changing. | * that are actually changing. | ||||
* | * | ||||
* the bits will be restored prior to actually setting | * the bits will be restored prior to actually setting | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | if (XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { | ||||
need_policy = TRUE; | need_policy = TRUE; | ||||
} else { | } else { | ||||
XVA_CLR_REQ(xvap, XAT_AV_QUARANTINED); | XVA_CLR_REQ(xvap, XAT_AV_QUARANTINED); | ||||
XVA_SET_REQ(&tmpxvattr, XAT_AV_QUARANTINED); | XVA_SET_REQ(&tmpxvattr, XAT_AV_QUARANTINED); | ||||
} | } | ||||
} | } | ||||
if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { | if (XVA_ISSET_REQ(xvap, XAT_REPARSE)) { | ||||
mutex_exit(&zp->z_lock); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EPERM)); | return (SET_ERROR(EPERM)); | ||||
} | } | ||||
if (need_policy == FALSE && | if (need_policy == FALSE && | ||||
(XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) || | (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP) || | ||||
XVA_ISSET_REQ(xvap, XAT_OPAQUE))) { | XVA_ISSET_REQ(xvap, XAT_OPAQUE))) { | ||||
need_policy = TRUE; | need_policy = TRUE; | ||||
} | } | ||||
} | } | ||||
mutex_exit(&zp->z_lock); | |||||
if (mask & AT_MODE) { | if (mask & AT_MODE) { | ||||
if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) { | if (zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr) == 0) { | ||||
err = secpolicy_setid_setsticky_clear(vp, vap, | err = secpolicy_setid_setsticky_clear(vp, vap, | ||||
&oldva, cr); | &oldva, cr); | ||||
if (err) { | if (err) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (err); | return (err); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (err == 0 && xattr_obj) { | ||||
goto out2; | goto out2; | ||||
} | } | ||||
if (mask & AT_UID) { | if (mask & AT_UID) { | ||||
new_uid = zfs_fuid_create(zfsvfs, | new_uid = zfs_fuid_create(zfsvfs, | ||||
(uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp); | (uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp); | ||||
if (new_uid != zp->z_uid && | if (new_uid != zp->z_uid && | ||||
zfs_fuid_overquota(zfsvfs, B_FALSE, new_uid)) { | zfs_fuid_overquota(zfsvfs, B_FALSE, new_uid)) { | ||||
if (attrzp) | if (attrzp) | ||||
VN_RELE(ZTOV(attrzp)); | vrele(ZTOV(attrzp)); | ||||
err = SET_ERROR(EDQUOT); | err = SET_ERROR(EDQUOT); | ||||
goto out2; | goto out2; | ||||
} | } | ||||
} | } | ||||
if (mask & AT_GID) { | if (mask & AT_GID) { | ||||
new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid, | new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid, | ||||
cr, ZFS_GROUP, &fuidp); | cr, ZFS_GROUP, &fuidp); | ||||
if (new_gid != zp->z_gid && | if (new_gid != zp->z_gid && | ||||
zfs_fuid_overquota(zfsvfs, B_TRUE, new_gid)) { | zfs_fuid_overquota(zfsvfs, B_TRUE, new_gid)) { | ||||
if (attrzp) | if (attrzp) | ||||
VN_RELE(ZTOV(attrzp)); | vrele(ZTOV(attrzp)); | ||||
err = SET_ERROR(EDQUOT); | err = SET_ERROR(EDQUOT); | ||||
goto out2; | goto out2; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
if (mask & AT_MODE) { | if (mask & AT_MODE) { | ||||
uint64_t pmode = zp->z_mode; | uint64_t pmode = zp->z_mode; | ||||
uint64_t acl_obj; | uint64_t acl_obj; | ||||
new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT); | new_mode = (pmode & S_IFMT) | (vap->va_mode & ~S_IFMT); | ||||
if (zp->z_zfsvfs->z_acl_mode == ZFS_ACL_RESTRICTED && | if (zp->z_zfsvfs->z_acl_mode == ZFS_ACL_RESTRICTED && | ||||
!(zp->z_pflags & ZFS_ACL_TRIVIAL)) { | !(zp->z_pflags & ZFS_ACL_TRIVIAL)) { | ||||
err = SET_ERROR(EPERM); | err = SET_ERROR(EPERM); | ||||
goto out; | goto out; | ||||
} | } | ||||
if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode)) | if (err = zfs_acl_chmod_setattr(zp, &aclp, new_mode)) | ||||
goto out; | goto out; | ||||
mutex_enter(&zp->z_lock); | |||||
if (!zp->z_is_sa && ((acl_obj = zfs_external_acl(zp)) != 0)) { | if (!zp->z_is_sa && ((acl_obj = zfs_external_acl(zp)) != 0)) { | ||||
/* | /* | ||||
* Are we upgrading ACL from old V0 format | * Are we upgrading ACL from old V0 format | ||||
* to V1 format? | * to V1 format? | ||||
*/ | */ | ||||
if (zfsvfs->z_version >= ZPL_VERSION_FUID && | if (zfsvfs->z_version >= ZPL_VERSION_FUID && | ||||
zfs_znode_acl_version(zp) == | zfs_znode_acl_version(zp) == | ||||
ZFS_ACL_VERSION_INITIAL) { | ZFS_ACL_VERSION_INITIAL) { | ||||
dmu_tx_hold_free(tx, acl_obj, 0, | dmu_tx_hold_free(tx, acl_obj, 0, | ||||
DMU_OBJECT_END); | DMU_OBJECT_END); | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | ||||
0, aclp->z_acl_bytes); | 0, aclp->z_acl_bytes); | ||||
} else { | } else { | ||||
dmu_tx_hold_write(tx, acl_obj, 0, | dmu_tx_hold_write(tx, acl_obj, 0, | ||||
aclp->z_acl_bytes); | aclp->z_acl_bytes); | ||||
} | } | ||||
} else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) { | } else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) { | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, | ||||
0, aclp->z_acl_bytes); | 0, aclp->z_acl_bytes); | ||||
} | } | ||||
mutex_exit(&zp->z_lock); | |||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | ||||
} else { | } else { | ||||
if ((mask & AT_XVATTR) && | if ((mask & AT_XVATTR) && | ||||
XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) | XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) | ||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); | ||||
else | else | ||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | ||||
} | } | ||||
Show All 16 Lines | zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, | ||||
/* | /* | ||||
* Set each attribute requested. | * Set each attribute requested. | ||||
* We group settings according to the locks they need to acquire. | * We group settings according to the locks they need to acquire. | ||||
* | * | ||||
* Note: you cannot set ctime directly, although it will be | * Note: you cannot set ctime directly, although it will be | ||||
* updated as a side-effect of calling this function. | * updated as a side-effect of calling this function. | ||||
*/ | */ | ||||
if (mask & (AT_UID|AT_GID|AT_MODE)) | if (mask & (AT_UID|AT_GID|AT_MODE)) | ||||
mutex_enter(&zp->z_acl_lock); | mutex_enter(&zp->z_acl_lock); | ||||
mutex_enter(&zp->z_lock); | |||||
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, | SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, | ||||
&zp->z_pflags, sizeof (zp->z_pflags)); | &zp->z_pflags, sizeof (zp->z_pflags)); | ||||
if (attrzp) { | if (attrzp) { | ||||
if (mask & (AT_UID|AT_GID|AT_MODE)) | if (mask & (AT_UID|AT_GID|AT_MODE)) | ||||
mutex_enter(&attrzp->z_acl_lock); | mutex_enter(&attrzp->z_acl_lock); | ||||
mutex_enter(&attrzp->z_lock); | |||||
SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, | SA_ADD_BULK_ATTR(xattr_bulk, xattr_count, | ||||
SA_ZPL_FLAGS(zfsvfs), NULL, &attrzp->z_pflags, | SA_ZPL_FLAGS(zfsvfs), NULL, &attrzp->z_pflags, | ||||
sizeof (attrzp->z_pflags)); | sizeof (attrzp->z_pflags)); | ||||
} | } | ||||
if (mask & (AT_UID|AT_GID)) { | if (mask & (AT_UID|AT_GID)) { | ||||
if (mask & AT_UID) { | if (mask & AT_UID) { | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr, | ||||
} | } | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_sync(zfsvfs, tx); | zfs_fuid_sync(zfsvfs, tx); | ||||
if (mask != 0) | if (mask != 0) | ||||
zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp); | zfs_log_setattr(zilog, tx, TX_SETATTR, zp, vap, mask, fuidp); | ||||
mutex_exit(&zp->z_lock); | |||||
if (mask & (AT_UID|AT_GID|AT_MODE)) | if (mask & (AT_UID|AT_GID|AT_MODE)) | ||||
mutex_exit(&zp->z_acl_lock); | mutex_exit(&zp->z_acl_lock); | ||||
if (attrzp) { | if (attrzp) { | ||||
if (mask & (AT_UID|AT_GID|AT_MODE)) | if (mask & (AT_UID|AT_GID|AT_MODE)) | ||||
mutex_exit(&attrzp->z_acl_lock); | mutex_exit(&attrzp->z_acl_lock); | ||||
mutex_exit(&attrzp->z_lock); | |||||
} | } | ||||
out: | out: | ||||
if (err == 0 && attrzp) { | if (err == 0 && attrzp) { | ||||
err2 = sa_bulk_update(attrzp->z_sa_hdl, xattr_bulk, | err2 = sa_bulk_update(attrzp->z_sa_hdl, xattr_bulk, | ||||
xattr_count, tx); | xattr_count, tx); | ||||
ASSERT(err2 == 0); | ASSERT(err2 == 0); | ||||
} | } | ||||
if (attrzp) | if (attrzp) | ||||
VN_RELE(ZTOV(attrzp)); | vrele(ZTOV(attrzp)); | ||||
if (aclp) | if (aclp) | ||||
zfs_acl_free(aclp); | zfs_acl_free(aclp); | ||||
if (fuidp) { | if (fuidp) { | ||||
zfs_fuid_info_free(fuidp); | zfs_fuid_info_free(fuidp); | ||||
fuidp = NULL; | fuidp = NULL; | ||||
} | } | ||||
if (err) { | if (err) { | ||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
if (err == ERESTART) | |||||
goto top; | |||||
} else { | } else { | ||||
err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); | err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
} | } | ||||
out2: | out2: | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (err); | return (err); | ||||
} | } | ||||
typedef struct zfs_zlock { | |||||
krwlock_t *zl_rwlock; /* lock we acquired */ | |||||
znode_t *zl_znode; /* znode we held */ | |||||
struct zfs_zlock *zl_next; /* next in list */ | |||||
} zfs_zlock_t; | |||||
/* | /* | ||||
* Drop locks and release vnodes that were held by zfs_rename_lock(). | * We acquire all but fdvp locks using non-blocking acquisitions. If we | ||||
* fail to acquire any lock in the path we will drop all held locks, | |||||
* acquire the new lock in a blocking fashion, and then release it and | |||||
* restart the rename. This acquire/release step ensures that we do not | |||||
* spin on a lock waiting for release. On error release all vnode locks | |||||
* and decrement references the way tmpfs_rename() would do. | |||||
*/ | */ | ||||
static void | static int | ||||
zfs_rename_unlock(zfs_zlock_t **zlpp) | zfs_rename_relock(struct vnode *sdvp, struct vnode **svpp, | ||||
struct vnode *tdvp, struct vnode **tvpp, | |||||
const struct componentname *scnp, const struct componentname *tcnp) | |||||
{ | { | ||||
zfs_zlock_t *zl; | struct vnode *nvp; | ||||
znode_t *sdzp, *tdzp, *szp, *tzp; | |||||
const char *snm = scnp->cn_nameptr; | |||||
const char *tnm = tcnp->cn_nameptr; | |||||
int error; | |||||
while ((zl = *zlpp) != NULL) { | VOP_UNLOCK(tdvp, 0); | ||||
if (zl->zl_znode != NULL) | if (*tvpp != NULL && *tvpp != tdvp) | ||||
VN_RELE(ZTOV(zl->zl_znode)); | VOP_UNLOCK(*tvpp, 0); | ||||
rw_exit(zl->zl_rwlock); | |||||
*zlpp = zl->zl_next; | relock: | ||||
kmem_free(zl, sizeof (*zl)); | error = vn_lock(sdvp, LK_EXCLUSIVE); | ||||
if (error) | |||||
goto out; | |||||
sdzp = VTOZ(sdvp); | |||||
error = vn_lock(tdvp, LK_EXCLUSIVE | LK_NOWAIT); | |||||
if (error != 0) { | |||||
VOP_UNLOCK(sdvp, 0); | |||||
if (error != EBUSY) | |||||
goto out; | |||||
error = vn_lock(tdvp, LK_EXCLUSIVE); | |||||
if (error) | |||||
goto out; | |||||
VOP_UNLOCK(tdvp, 0); | |||||
goto relock; | |||||
} | } | ||||
} | tdzp = VTOZ(tdvp); | ||||
/* | /* | ||||
* Search back through the directory tree, using the ".." entries. | * Re-resolve svp to be certain it still exists and fetch the | ||||
* Lock each directory in the chain to prevent concurrent renames. | * correct vnode. | ||||
* Fail any attempt to move a directory into one of its own descendants. | |||||
* XXX - z_parent_lock can overlap with map or grow locks | |||||
*/ | */ | ||||
static int | error = zfs_dirent_lookup(sdzp, snm, &szp, ZEXISTS); | ||||
zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp) | if (error != 0) { | ||||
{ | /* Source entry invalid or not there. */ | ||||
zfs_zlock_t *zl; | VOP_UNLOCK(sdvp, 0); | ||||
znode_t *zp = tdzp; | VOP_UNLOCK(tdvp, 0); | ||||
uint64_t rootid = zp->z_zfsvfs->z_root; | if ((scnp->cn_flags & ISDOTDOT) != 0 || | ||||
uint64_t oidp = zp->z_id; | (scnp->cn_namelen == 1 && scnp->cn_nameptr[0] == '.')) | ||||
krwlock_t *rwlp = &szp->z_parent_lock; | error = SET_ERROR(EINVAL); | ||||
krw_t rw = RW_WRITER; | goto out; | ||||
} | |||||
nvp = ZTOV(szp); | |||||
error = vn_lock(nvp, LK_EXCLUSIVE | LK_NOWAIT); | |||||
if (error != 0) { | |||||
VOP_UNLOCK(sdvp, 0); | |||||
VOP_UNLOCK(tdvp, 0); | |||||
if (error != EBUSY) | |||||
goto out; | |||||
error = vn_lock(nvp, LK_EXCLUSIVE); | |||||
if (error != 0) | |||||
goto out; | |||||
VOP_UNLOCK(nvp, 0); | |||||
/* | /* | ||||
* First pass write-locks szp and compares to zp->z_id. | * Concurrent rename race. | ||||
* Later passes read-lock zp and compare to zp->z_parent. | |||||
*/ | */ | ||||
do { | if (nvp == tdvp) { | ||||
if (!rw_tryenter(rwlp, rw)) { | vrele(nvp); | ||||
error = SET_ERROR(EINVAL); | |||||
goto out; | |||||
} | |||||
vrele(*svpp); | |||||
*svpp = nvp; | |||||
goto relock; | |||||
} | |||||
vrele(*svpp); | |||||
*svpp = nvp; | |||||
/* | /* | ||||
* Another thread is renaming in this path. | * Re-resolve tvp and acquire the vnode lock if present. | ||||
* Note that if we are a WRITER, we don't have any | * If tvp disappeared we just carry on. | ||||
* parent_locks held yet. | |||||
*/ | */ | ||||
if (rw == RW_READER && zp->z_id > szp->z_id) { | error = zfs_dirent_lookup(tdzp, tnm, &tzp, 0); | ||||
/* | if (error) { | ||||
* Drop our locks and restart | VOP_UNLOCK(sdvp, 0); | ||||
*/ | VOP_UNLOCK(tdvp, 0); | ||||
zfs_rename_unlock(&zl); | VOP_UNLOCK(*svpp, 0); | ||||
*zlpp = NULL; | if ((tcnp->cn_flags & ISDOTDOT) != 0) | ||||
zp = tdzp; | error = SET_ERROR(EINVAL); | ||||
oidp = zp->z_id; | goto out; | ||||
rwlp = &szp->z_parent_lock; | |||||
rw = RW_WRITER; | |||||
continue; | |||||
} else { | |||||
/* | |||||
* Wait for other thread to drop its locks | |||||
*/ | |||||
rw_enter(rwlp, rw); | |||||
} | } | ||||
if (*tvpp != NULL) | |||||
vrele(*tvpp); | |||||
*tvpp = NULL; | |||||
if (tzp != NULL) { | |||||
nvp = ZTOV(tzp); | |||||
error = vn_lock(nvp, LK_EXCLUSIVE | LK_NOWAIT); | |||||
if (error != 0) { | |||||
VOP_UNLOCK(sdvp, 0); | |||||
VOP_UNLOCK(tdvp, 0); | |||||
VOP_UNLOCK(*svpp, 0); | |||||
if (error != EBUSY) | |||||
goto out; | |||||
error = vn_lock(nvp, LK_EXCLUSIVE); | |||||
if (error != 0) | |||||
goto out; | |||||
vput(nvp); | |||||
goto relock; | |||||
} | } | ||||
*tvpp = nvp; | |||||
} | |||||
zl = kmem_alloc(sizeof (*zl), KM_SLEEP); | |||||
zl->zl_rwlock = rwlp; | |||||
zl->zl_znode = NULL; | |||||
zl->zl_next = *zlpp; | |||||
*zlpp = zl; | |||||
if (oidp == szp->z_id) /* We're a descendant of szp */ | |||||
return (SET_ERROR(EINVAL)); | |||||
if (oidp == rootid) /* We've hit the top */ | |||||
return (0); | return (0); | ||||
if (rw == RW_READER) { /* i.e. not the first pass */ | out: | ||||
int error = zfs_zget(zp->z_zfsvfs, oidp, &zp); | |||||
if (error) | |||||
return (error); | return (error); | ||||
zl->zl_znode = zp; | |||||
} | } | ||||
(void) sa_lookup(zp->z_sa_hdl, SA_ZPL_PARENT(zp->z_zfsvfs), | |||||
&oidp, sizeof (oidp)); | |||||
rwlp = &zp->z_parent_lock; | |||||
rw = RW_READER; | |||||
} while (zp->z_id != sdzp->z_id); | /* | ||||
* Note that we must use VRELE_ASYNC in this function as it walks | |||||
* up the directory tree and vrele may need to acquire an exclusive | |||||
* lock if a last reference to a vnode is dropped. | |||||
*/ | |||||
static int | |||||
zfs_rename_check(znode_t *szp, znode_t *sdzp, znode_t *tdzp) | |||||
{ | |||||
zfsvfs_t *zfsvfs; | |||||
znode_t *zp, *zp1; | |||||
uint64_t parent; | |||||
int error; | |||||
zfsvfs = tdzp->z_zfsvfs; | |||||
if (tdzp == szp) | |||||
return (SET_ERROR(EINVAL)); | |||||
if (tdzp == sdzp) | |||||
return (0); | return (0); | ||||
if (tdzp->z_id == zfsvfs->z_root) | |||||
return (0); | |||||
zp = tdzp; | |||||
for (;;) { | |||||
ASSERT(!zp->z_unlinked); | |||||
if ((error = sa_lookup(zp->z_sa_hdl, | |||||
SA_ZPL_PARENT(zfsvfs), &parent, sizeof (parent))) != 0) | |||||
break; | |||||
if (parent == szp->z_id) { | |||||
error = SET_ERROR(EINVAL); | |||||
break; | |||||
} | } | ||||
if (parent == zfsvfs->z_root) | |||||
break; | |||||
if (parent == sdzp->z_id) | |||||
break; | |||||
error = zfs_zget(zfsvfs, parent, &zp1); | |||||
if (error != 0) | |||||
break; | |||||
if (zp != tdzp) | |||||
VN_RELE_ASYNC(ZTOV(zp), | |||||
dsl_pool_vnrele_taskq(dmu_objset_pool(zfsvfs->z_os))); | |||||
zp = zp1; | |||||
} | |||||
if (error == ENOTDIR) | |||||
panic("checkpath: .. not a directory\n"); | |||||
if (zp != tdzp) | |||||
VN_RELE_ASYNC(ZTOV(zp), | |||||
dsl_pool_vnrele_taskq(dmu_objset_pool(zfsvfs->z_os))); | |||||
return (error); | |||||
} | |||||
/* | /* | ||||
* Move an entry from the provided source directory to the target | * Move an entry from the provided source directory to the target | ||||
* directory. Change the entry name as indicated. | * directory. Change the entry name as indicated. | ||||
* | * | ||||
* IN: sdvp - Source directory containing the "old entry". | * IN: sdvp - Source directory containing the "old entry". | ||||
* snm - Old entry name. | * snm - Old entry name. | ||||
* tdvp - Target directory to contain the "new entry". | * tdvp - Target directory to contain the "new entry". | ||||
* tnm - New entry name. | * tnm - New entry name. | ||||
* cr - credentials of caller. | * cr - credentials of caller. | ||||
* ct - caller context | * ct - caller context | ||||
* flags - case flags | * flags - case flags | ||||
* | * | ||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* sdvp,tdvp - ctime|mtime updated | * sdvp,tdvp - ctime|mtime updated | ||||
*/ | */ | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr, | zfs_rename(vnode_t *sdvp, vnode_t **svpp, struct componentname *scnp, | ||||
caller_context_t *ct, int flags) | vnode_t *tdvp, vnode_t **tvpp, struct componentname *tcnp, | ||||
cred_t *cr) | |||||
{ | { | ||||
znode_t *tdzp, *sdzp, *szp, *tzp; | |||||
zfsvfs_t *zfsvfs; | zfsvfs_t *zfsvfs; | ||||
znode_t *sdzp, *tdzp, *szp, *tzp; | |||||
zilog_t *zilog; | zilog_t *zilog; | ||||
vnode_t *realvp; | |||||
zfs_dirlock_t *sdl, *tdl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
zfs_zlock_t *zl; | char *snm = scnp->cn_nameptr; | ||||
int cmp, serr, terr; | char *tnm = tcnp->cn_nameptr; | ||||
int error = 0; | int error = 0; | ||||
int zflg = 0; | |||||
boolean_t waited = B_FALSE; | |||||
tdzp = VTOZ(tdvp); | tdzp = VTOZ(tdvp); | ||||
ZFS_VERIFY_ZP(tdzp); | ZFS_VERIFY_ZP(tdzp); | ||||
zfsvfs = tdzp->z_zfsvfs; | zfsvfs = tdzp->z_zfsvfs; | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
sdzp = VTOZ(sdvp); | sdzp = VTOZ(sdvp); | ||||
if ((*svpp)->v_mount != tdvp->v_mount || | |||||
((*tvpp) != NULL && (*svpp)->v_mount != (*tvpp)->v_mount)) { | |||||
error = SET_ERROR(EXDEV); | |||||
goto out; | |||||
} | |||||
/* | /* | ||||
* In case sdzp is not valid, let's be sure to exit from the right | * In case sdzp is not valid, let's be sure to exit from the right | ||||
* zfsvfs_t. | * zfsvfs_t. | ||||
*/ | */ | ||||
if (sdzp->z_sa_hdl == NULL) { | if (sdzp->z_sa_hdl == NULL) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EIO)); | return (SET_ERROR(EIO)); | ||||
} | } | ||||
/* | if (zfsctl_is_node(tdvp)) { | ||||
* We check z_zfsvfs rather than v_vfsp here, because snapshots and the | error = SET_ERROR(EXDEV); | ||||
* ctldir appear to have the same v_vfsp. | goto out; | ||||
*/ | |||||
if (sdzp->z_zfsvfs != zfsvfs || zfsctl_is_node(tdvp)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EXDEV)); | |||||
} | } | ||||
if (zfsvfs->z_utf8 && u8_validate(tnm, | if (zfsvfs->z_utf8 && u8_validate(tnm, | ||||
strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | strlen(tnm), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | error = SET_ERROR(EILSEQ); | ||||
return (SET_ERROR(EILSEQ)); | goto out; | ||||
} | } | ||||
if (flags & FIGNORECASE) | |||||
zflg |= ZCILOOK; | |||||
top: | |||||
szp = NULL; | |||||
tzp = NULL; | |||||
zl = NULL; | |||||
/* | /* | ||||
* This is to prevent the creation of links into attribute space | * All all four vnodes to ensure safety and semantics of renaming. | ||||
* by renaming a linked file into/outof an attribute directory. | |||||
* See the comment in zfs_link() for why this is considered bad. | |||||
*/ | */ | ||||
if ((tdzp->z_pflags & ZFS_XATTR) != (sdzp->z_pflags & ZFS_XATTR)) { | error = zfs_rename_relock(sdvp, svpp, tdvp, tvpp, scnp, tcnp); | ||||
if (error != 0) { | |||||
/* no vnodes are locked in the case of error here */ | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EINVAL)); | return (error); | ||||
} | } | ||||
/* | /* If source and target are the same file, there is nothing to do. */ | ||||
* Lock source and target directory entries. To prevent deadlock, | if ((*svpp) == (*tvpp)) { | ||||
* a lock ordering must be defined. We lock the directory with | error = 0; | ||||
* the smallest object id first, or if it's a tie, the one with | goto unlockout; | ||||
* the lexically first name. | |||||
*/ | |||||
if (sdzp->z_id < tdzp->z_id) { | |||||
cmp = -1; | |||||
} else if (sdzp->z_id > tdzp->z_id) { | |||||
cmp = 1; | |||||
} else { | |||||
/* | |||||
* First compare the two name arguments without | |||||
* considering any case folding. | |||||
*/ | |||||
int nofold = (zfsvfs->z_norm & ~U8_TEXTPREP_TOUPPER); | |||||
cmp = u8_strcmp(snm, tnm, 0, nofold, U8_UNICODE_LATEST, &error); | |||||
ASSERT(error == 0 || !zfsvfs->z_utf8); | |||||
if (cmp == 0) { | |||||
/* | |||||
* POSIX: "If the old argument and the new argument | |||||
* both refer to links to the same existing file, | |||||
* the rename() function shall return successfully | |||||
* and perform no other action." | |||||
*/ | |||||
ZFS_EXIT(zfsvfs); | |||||
return (0); | |||||
} | } | ||||
/* | |||||
* If the file system is case-folding, then we may | |||||
* have some more checking to do. A case-folding file | |||||
* system is either supporting mixed case sensitivity | |||||
* access or is completely case-insensitive. Note | |||||
* that the file system is always case preserving. | |||||
* | |||||
* In mixed sensitivity mode case sensitive behavior | |||||
* is the default. FIGNORECASE must be used to | |||||
* explicitly request case insensitive behavior. | |||||
* | |||||
* If the source and target names provided differ only | |||||
* by case (e.g., a request to rename 'tim' to 'Tim'), | |||||
* we will treat this as a special case in the | |||||
* case-insensitive mode: as long as the source name | |||||
* is an exact match, we will allow this to proceed as | |||||
* a name-change request. | |||||
*/ | |||||
if ((zfsvfs->z_case == ZFS_CASE_INSENSITIVE || | |||||
(zfsvfs->z_case == ZFS_CASE_MIXED && | |||||
flags & FIGNORECASE)) && | |||||
u8_strcmp(snm, tnm, 0, zfsvfs->z_norm, U8_UNICODE_LATEST, | |||||
&error) == 0) { | |||||
/* | |||||
* case preserving rename request, require exact | |||||
* name matches | |||||
*/ | |||||
zflg |= ZCIEXACT; | |||||
zflg &= ~ZCILOOK; | |||||
} | |||||
} | |||||
/* | if (((*svpp)->v_type == VDIR && (*svpp)->v_mountedhere != NULL) || | ||||
* If the source and destination directories are the same, we should | ((*tvpp) != NULL && (*tvpp)->v_type == VDIR && | ||||
* grab the z_name_lock of that directory only once. | (*tvpp)->v_mountedhere != NULL)) { | ||||
*/ | error = SET_ERROR(EXDEV); | ||||
if (sdzp == tdzp) { | goto unlockout; | ||||
zflg |= ZHAVELOCK; | |||||
rw_enter(&sdzp->z_name_lock, RW_READER); | |||||
} | } | ||||
if (cmp < 0) { | sdzp = VTOZ(sdvp); | ||||
serr = zfs_dirent_lock(&sdl, sdzp, snm, &szp, | ZFS_VERIFY_ZP(sdzp); | ||||
ZEXISTS | zflg, NULL, NULL); | szp = VTOZ(*svpp); | ||||
terr = zfs_dirent_lock(&tdl, | tzp = *tvpp == NULL ? NULL : VTOZ(*tvpp); | ||||
tdzp, tnm, &tzp, ZRENAMING | zflg, NULL, NULL); | |||||
} else { | |||||
terr = zfs_dirent_lock(&tdl, | |||||
tdzp, tnm, &tzp, zflg, NULL, NULL); | |||||
serr = zfs_dirent_lock(&sdl, | |||||
sdzp, snm, &szp, ZEXISTS | ZRENAMING | zflg, | |||||
NULL, NULL); | |||||
} | |||||
if (serr) { | |||||
/* | /* | ||||
* Source entry invalid or not there. | * This is to prevent the creation of links into attribute space | ||||
* by renaming a linked file into/outof an attribute directory. | |||||
* See the comment in zfs_link() for why this is considered bad. | |||||
*/ | */ | ||||
if (!terr) { | if ((tdzp->z_pflags & ZFS_XATTR) != (sdzp->z_pflags & ZFS_XATTR)) { | ||||
zfs_dirent_unlock(tdl); | error = SET_ERROR(EINVAL); | ||||
if (tzp) | goto unlockout; | ||||
VN_RELE(ZTOV(tzp)); | |||||
} | } | ||||
if (sdzp == tdzp) | |||||
rw_exit(&sdzp->z_name_lock); | |||||
/* | /* | ||||
* FreeBSD: In OpenSolaris they only check if rename source is | |||||
* ".." here, because "." is handled in their lookup. This is | |||||
* not the case for FreeBSD, so we check for "." explicitly. | |||||
*/ | |||||
if (strcmp(snm, ".") == 0 || strcmp(snm, "..") == 0) | |||||
serr = SET_ERROR(EINVAL); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (serr); | |||||
} | |||||
if (terr) { | |||||
zfs_dirent_unlock(sdl); | |||||
VN_RELE(ZTOV(szp)); | |||||
if (sdzp == tdzp) | |||||
rw_exit(&sdzp->z_name_lock); | |||||
if (strcmp(tnm, "..") == 0) | |||||
terr = SET_ERROR(EINVAL); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (terr); | |||||
} | |||||
/* | |||||
* Must have write access at the source to remove the old entry | * Must have write access at the source to remove the old entry | ||||
* and write access at the target to create the new entry. | * and write access at the target to create the new entry. | ||||
* Note that if target and source are the same, this can be | * Note that if target and source are the same, this can be | ||||
* done in a single check. | * done in a single check. | ||||
*/ | */ | ||||
if (error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)) | if (error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr)) | ||||
goto out; | goto unlockout; | ||||
if (ZTOV(szp)->v_type == VDIR) { | if ((*svpp)->v_type == VDIR) { | ||||
/* | /* | ||||
* Avoid ".", "..", and aliases of "." for obvious reasons. | |||||
*/ | |||||
if ((scnp->cn_namelen == 1 && scnp->cn_nameptr[0] == '.') || | |||||
sdzp == szp || | |||||
(scnp->cn_flags | tcnp->cn_flags) & ISDOTDOT) { | |||||
error = EINVAL; | |||||
goto unlockout; | |||||
} | |||||
/* | |||||
* Check to make sure rename is valid. | * Check to make sure rename is valid. | ||||
* Can't do a move like this: /usr/a/b to /usr/a/b/c/d | * Can't do a move like this: /usr/a/b to /usr/a/b/c/d | ||||
*/ | */ | ||||
if (error = zfs_rename_lock(szp, tdzp, sdzp, &zl)) | if (error = zfs_rename_check(szp, sdzp, tdzp)) | ||||
goto out; | goto unlockout; | ||||
} | } | ||||
/* | /* | ||||
* Does target exist? | * Does target exist? | ||||
*/ | */ | ||||
if (tzp) { | if (tzp) { | ||||
/* | /* | ||||
* Source and target must be the same type. | * Source and target must be the same type. | ||||
*/ | */ | ||||
if (ZTOV(szp)->v_type == VDIR) { | if ((*svpp)->v_type == VDIR) { | ||||
if (ZTOV(tzp)->v_type != VDIR) { | if ((*tvpp)->v_type != VDIR) { | ||||
error = SET_ERROR(ENOTDIR); | error = SET_ERROR(ENOTDIR); | ||||
goto out; | goto unlockout; | ||||
} else { | |||||
cache_purge(tdvp); | |||||
if (sdvp != tdvp) | |||||
cache_purge(sdvp); | |||||
} | } | ||||
} else { | } else { | ||||
if (ZTOV(tzp)->v_type == VDIR) { | if ((*tvpp)->v_type == VDIR) { | ||||
error = SET_ERROR(EISDIR); | error = SET_ERROR(EISDIR); | ||||
goto out; | goto unlockout; | ||||
} | } | ||||
} | } | ||||
/* | |||||
* POSIX dictates that when the source and target | |||||
* entries refer to the same file object, rename | |||||
* must do nothing and exit without error. | |||||
*/ | |||||
if (szp->z_id == tzp->z_id) { | |||||
error = 0; | |||||
goto out; | |||||
} | } | ||||
} | |||||
vnevent_rename_src(ZTOV(szp), sdvp, snm, ct); | vnevent_rename_src(*svpp, sdvp, scnp->cn_nameptr, ct); | ||||
if (tzp) | if (tzp) | ||||
vnevent_rename_dest(ZTOV(tzp), tdvp, tnm, ct); | vnevent_rename_dest(*tvpp, tdvp, tnm, ct); | ||||
/* | /* | ||||
* notify the target directory if it is not the same | * notify the target directory if it is not the same | ||||
* as source directory. | * as source directory. | ||||
*/ | */ | ||||
if (tdvp != sdvp) { | if (tdvp != sdvp) { | ||||
vnevent_rename_dest_dir(tdvp, ct); | vnevent_rename_dest_dir(tdvp, ct); | ||||
} | } | ||||
Show All 9 Lines | zfs_rename(vnode_t *sdvp, vnode_t **svpp, struct componentname *scnp, | ||||
} | } | ||||
if (tzp) { | if (tzp) { | ||||
dmu_tx_hold_sa(tx, tzp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, tzp->z_sa_hdl, B_FALSE); | ||||
zfs_sa_upgrade_txholds(tx, tzp); | zfs_sa_upgrade_txholds(tx, tzp); | ||||
} | } | ||||
zfs_sa_upgrade_txholds(tx, szp); | zfs_sa_upgrade_txholds(tx, szp); | ||||
dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
if (zl != NULL) | |||||
zfs_rename_unlock(&zl); | |||||
zfs_dirent_unlock(sdl); | |||||
zfs_dirent_unlock(tdl); | |||||
if (sdzp == tdzp) | |||||
rw_exit(&sdzp->z_name_lock); | |||||
VN_RELE(ZTOV(szp)); | |||||
if (tzp) | |||||
VN_RELE(ZTOV(tzp)); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
goto top; | goto unlockout; | ||||
} | } | ||||
dmu_tx_abort(tx); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
if (tzp) /* Attempt to remove the existing target */ | if (tzp) /* Attempt to remove the existing target */ | ||||
error = zfs_link_destroy(tdl, tzp, tx, zflg, NULL); | error = zfs_link_destroy(tdzp, tnm, tzp, tx, 0, NULL); | ||||
if (error == 0) { | if (error == 0) { | ||||
error = zfs_link_create(tdl, szp, tx, ZRENAMING); | error = zfs_link_create(tdzp, tnm, szp, tx, ZRENAMING); | ||||
if (error == 0) { | if (error == 0) { | ||||
szp->z_pflags |= ZFS_AV_MODIFIED; | szp->z_pflags |= ZFS_AV_MODIFIED; | ||||
error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs), | error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs), | ||||
(void *)&szp->z_pflags, sizeof (uint64_t), tx); | (void *)&szp->z_pflags, sizeof (uint64_t), tx); | ||||
ASSERT0(error); | ASSERT0(error); | ||||
error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL); | error = zfs_link_destroy(sdzp, snm, szp, tx, ZRENAMING, | ||||
NULL); | |||||
if (error == 0) { | if (error == 0) { | ||||
zfs_log_rename(zilog, tx, TX_RENAME | | zfs_log_rename(zilog, tx, TX_RENAME, sdzp, | ||||
(flags & FIGNORECASE ? TX_CI : 0), sdzp, | snm, tdzp, tnm, szp); | ||||
sdl->dl_name, tdzp, tdl->dl_name, szp); | |||||
/* | /* | ||||
* Update path information for the target vnode | * Update path information for the target vnode | ||||
*/ | */ | ||||
vn_renamepath(tdvp, ZTOV(szp), tnm, | vn_renamepath(tdvp, *svpp, tnm, strlen(tnm)); | ||||
strlen(tnm)); | |||||
} else { | } else { | ||||
/* | /* | ||||
* At this point, we have successfully created | * At this point, we have successfully created | ||||
* the target name, but have failed to remove | * the target name, but have failed to remove | ||||
* the source name. Since the create was done | * the source name. Since the create was done | ||||
* with the ZRENAMING flag, there are | * with the ZRENAMING flag, there are | ||||
* complications; for one, the link count is | * complications; for one, the link count is | ||||
* wrong. The easiest way to deal with this | * wrong. The easiest way to deal with this | ||||
* is to remove the newly created target, and | * is to remove the newly created target, and | ||||
* return the original error. This must | * return the original error. This must | ||||
* succeed; fortunately, it is very unlikely to | * succeed; fortunately, it is very unlikely to | ||||
* fail, since we just created it. | * fail, since we just created it. | ||||
*/ | */ | ||||
VERIFY3U(zfs_link_destroy(tdl, szp, tx, | VERIFY3U(zfs_link_destroy(tdzp, tnm, szp, tx, | ||||
ZRENAMING, NULL), ==, 0); | ZRENAMING, NULL), ==, 0); | ||||
} | } | ||||
} | } | ||||
#ifdef FREEBSD_NAMECACHE | |||||
if (error == 0) { | if (error == 0) { | ||||
cache_purge(sdvp); | cache_purge(*svpp); | ||||
cache_purge(tdvp); | if (*tvpp != NULL) | ||||
cache_purge(ZTOV(szp)); | cache_purge(*tvpp); | ||||
if (tzp) | cache_purge_negative(tdvp); | ||||
cache_purge(ZTOV(tzp)); | |||||
} | } | ||||
#endif | |||||
} | } | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
out: | |||||
if (zl != NULL) | |||||
zfs_rename_unlock(&zl); | |||||
zfs_dirent_unlock(sdl); | unlockout: /* all four vnodes are locked */ | ||||
zfs_dirent_unlock(tdl); | VOP_UNLOCK(*svpp, 0); | ||||
VOP_UNLOCK(sdvp, 0); | |||||
if (sdzp == tdzp) | out: /* original two vnodes are locked */ | ||||
rw_exit(&sdzp->z_name_lock); | ZFS_EXIT(zfsvfs); | ||||
VN_RELE(ZTOV(szp)); | |||||
if (tzp) | |||||
VN_RELE(ZTOV(tzp)); | |||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | if (*tvpp != NULL) | ||||
VOP_UNLOCK(*tvpp, 0); | |||||
if (tdvp != *tvpp) | |||||
VOP_UNLOCK(tdvp, 0); | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Insert the indicated symbolic reference entry into the directory. | * Insert the indicated symbolic reference entry into the directory. | ||||
* | * | ||||
* IN: dvp - Directory to contain new symbolic link. | * IN: dvp - Directory to contain new symbolic link. | ||||
* link - Name for new symlink entry. | * link - Name for new symlink entry. | ||||
* vap - Attributes of new entry. | * vap - Attributes of new entry. | ||||
* cr - credentials of caller. | * cr - credentials of caller. | ||||
* ct - caller context | * ct - caller context | ||||
* flags - case flags | * flags - case flags | ||||
* | * | ||||
* RETURN: 0 on success, error code on failure. | * RETURN: 0 on success, error code on failure. | ||||
* | * | ||||
* Timestamps: | * Timestamps: | ||||
* dvp - ctime|mtime updated | * dvp - ctime|mtime updated | ||||
*/ | */ | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_symlink(vnode_t *dvp, vnode_t **vpp, char *name, vattr_t *vap, char *link, | zfs_symlink(vnode_t *dvp, vnode_t **vpp, char *name, vattr_t *vap, char *link, | ||||
cred_t *cr, kthread_t *td) | cred_t *cr, kthread_t *td) | ||||
{ | { | ||||
znode_t *zp, *dzp = VTOZ(dvp); | znode_t *zp, *dzp = VTOZ(dvp); | ||||
zfs_dirlock_t *dl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
uint64_t len = strlen(link); | uint64_t len = strlen(link); | ||||
int error; | int error; | ||||
int zflg = ZNEW; | |||||
zfs_acl_ids_t acl_ids; | zfs_acl_ids_t acl_ids; | ||||
boolean_t fuid_dirtied; | boolean_t fuid_dirtied; | ||||
uint64_t txtype = TX_SYMLINK; | uint64_t txtype = TX_SYMLINK; | ||||
boolean_t waited = B_FALSE; | |||||
int flags = 0; | int flags = 0; | ||||
ASSERT(vap->va_type == VLNK); | ASSERT(vap->va_type == VLNK); | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(dzp); | ZFS_VERIFY_ZP(dzp); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
if (zfsvfs->z_utf8 && u8_validate(name, strlen(name), | if (zfsvfs->z_utf8 && u8_validate(name, strlen(name), | ||||
NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EILSEQ)); | return (SET_ERROR(EILSEQ)); | ||||
} | } | ||||
if (flags & FIGNORECASE) | |||||
zflg |= ZCILOOK; | |||||
if (len > MAXPATHLEN) { | if (len > MAXPATHLEN) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(ENAMETOOLONG)); | return (SET_ERROR(ENAMETOOLONG)); | ||||
} | } | ||||
if ((error = zfs_acl_ids_create(dzp, 0, | if ((error = zfs_acl_ids_create(dzp, 0, | ||||
vap, cr, NULL, &acl_ids)) != 0) { | vap, cr, NULL, &acl_ids)) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
getnewvnode_reserve(1); | |||||
top: | |||||
/* | /* | ||||
* Attempt to lock directory; fail if entry already exists. | * Attempt to lock directory; fail if entry already exists. | ||||
*/ | */ | ||||
error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL); | error = zfs_dirent_lookup(dzp, name, &zp, ZNEW); | ||||
if (error) { | if (error) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
zfs_dirent_unlock(dl); | |||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
zfs_dirent_unlock(dl); | |||||
getnewvnode_drop_reserve(); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EDQUOT)); | return (SET_ERROR(EDQUOT)); | ||||
} | } | ||||
getnewvnode_reserve(1); | |||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
fuid_dirtied = zfsvfs->z_fuid_dirty; | fuid_dirtied = zfsvfs->z_fuid_dirty; | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len)); | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, MAX(1, len)); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | ||||
dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + | ||||
ZFS_SA_BASE_ATTR_SIZE + len); | ZFS_SA_BASE_ATTR_SIZE + len); | ||||
dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); | ||||
if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | if (!zfsvfs->z_use_sa && acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) { | ||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, | dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, | ||||
acl_ids.z_aclp->z_acl_bytes); | acl_ids.z_aclp->z_acl_bytes); | ||||
} | } | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_txhold(zfsvfs, tx); | zfs_fuid_txhold(zfsvfs, tx); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
zfs_dirent_unlock(dl); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | |||||
goto top; | |||||
} | |||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Create a new object for the symlink. | * Create a new object for the symlink. | ||||
* for version 4 ZPL datsets the symlink will be an SA attribute | * for version 4 ZPL datsets the symlink will be an SA attribute | ||||
*/ | */ | ||||
zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids); | ||||
if (fuid_dirtied) | if (fuid_dirtied) | ||||
zfs_fuid_sync(zfsvfs, tx); | zfs_fuid_sync(zfsvfs, tx); | ||||
mutex_enter(&zp->z_lock); | |||||
if (zp->z_is_sa) | if (zp->z_is_sa) | ||||
error = sa_update(zp->z_sa_hdl, SA_ZPL_SYMLINK(zfsvfs), | error = sa_update(zp->z_sa_hdl, SA_ZPL_SYMLINK(zfsvfs), | ||||
link, len, tx); | link, len, tx); | ||||
else | else | ||||
zfs_sa_symlink(zp, link, len, tx); | zfs_sa_symlink(zp, link, len, tx); | ||||
mutex_exit(&zp->z_lock); | |||||
zp->z_size = len; | zp->z_size = len; | ||||
(void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs), | (void) sa_update(zp->z_sa_hdl, SA_ZPL_SIZE(zfsvfs), | ||||
&zp->z_size, sizeof (zp->z_size), tx); | &zp->z_size, sizeof (zp->z_size), tx); | ||||
/* | /* | ||||
* Insert the new object into the directory. | * Insert the new object into the directory. | ||||
*/ | */ | ||||
(void) zfs_link_create(dl, zp, tx, ZNEW); | (void) zfs_link_create(dzp, name, zp, tx, ZNEW); | ||||
if (flags & FIGNORECASE) | |||||
txtype |= TX_CI; | |||||
zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); | zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link); | ||||
*vpp = ZTOV(zp); | *vpp = ZTOV(zp); | ||||
zfs_acl_ids_free(&acl_ids); | zfs_acl_ids_free(&acl_ids); | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
getnewvnode_drop_reserve(); | getnewvnode_drop_reserve(); | ||||
zfs_dirent_unlock(dl); | |||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
Show All 18 Lines | |||||
{ | { | ||||
znode_t *zp = VTOZ(vp); | znode_t *zp = VTOZ(vp); | ||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | zfsvfs_t *zfsvfs = zp->z_zfsvfs; | ||||
int error; | int error; | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(zp); | ZFS_VERIFY_ZP(zp); | ||||
mutex_enter(&zp->z_lock); | |||||
if (zp->z_is_sa) | if (zp->z_is_sa) | ||||
error = sa_lookup_uio(zp->z_sa_hdl, | error = sa_lookup_uio(zp->z_sa_hdl, | ||||
SA_ZPL_SYMLINK(zfsvfs), uio); | SA_ZPL_SYMLINK(zfsvfs), uio); | ||||
else | else | ||||
error = zfs_sa_readlink(zp, uio); | error = zfs_sa_readlink(zp, uio); | ||||
mutex_exit(&zp->z_lock); | |||||
ZFS_ACCESSTIME_STAMP(zfsvfs, zp); | ZFS_ACCESSTIME_STAMP(zfsvfs, zp); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
Show All 15 Lines | |||||
static int | static int | ||||
zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr, | zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr, | ||||
caller_context_t *ct, int flags) | caller_context_t *ct, int flags) | ||||
{ | { | ||||
znode_t *dzp = VTOZ(tdvp); | znode_t *dzp = VTOZ(tdvp); | ||||
znode_t *tzp, *szp; | znode_t *tzp, *szp; | ||||
zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | zfsvfs_t *zfsvfs = dzp->z_zfsvfs; | ||||
zilog_t *zilog; | zilog_t *zilog; | ||||
zfs_dirlock_t *dl; | |||||
dmu_tx_t *tx; | dmu_tx_t *tx; | ||||
vnode_t *realvp; | |||||
int error; | int error; | ||||
int zf = ZNEW; | |||||
uint64_t parent; | uint64_t parent; | ||||
uid_t owner; | uid_t owner; | ||||
boolean_t waited = B_FALSE; | |||||
ASSERT(tdvp->v_type == VDIR); | ASSERT(tdvp->v_type == VDIR); | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(dzp); | ZFS_VERIFY_ZP(dzp); | ||||
zilog = zfsvfs->z_log; | zilog = zfsvfs->z_log; | ||||
if (VOP_REALVP(svp, &realvp, ct) == 0) | |||||
svp = realvp; | |||||
/* | /* | ||||
* POSIX dictates that we return EPERM here. | * POSIX dictates that we return EPERM here. | ||||
* Better choices include ENOTSUP or EISDIR. | * Better choices include ENOTSUP or EISDIR. | ||||
*/ | */ | ||||
if (svp->v_type == VDIR) { | if (svp->v_type == VDIR) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EPERM)); | return (SET_ERROR(EPERM)); | ||||
} | } | ||||
szp = VTOZ(svp); | szp = VTOZ(svp); | ||||
ZFS_VERIFY_ZP(szp); | ZFS_VERIFY_ZP(szp); | ||||
if (szp->z_pflags & (ZFS_APPENDONLY | ZFS_IMMUTABLE | ZFS_READONLY)) { | if (szp->z_pflags & (ZFS_APPENDONLY | ZFS_IMMUTABLE | ZFS_READONLY)) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EPERM)); | return (SET_ERROR(EPERM)); | ||||
} | } | ||||
/* | szp = VTOZ(svp); | ||||
* We check z_zfsvfs rather than v_vfsp here, because snapshots and the | ZFS_VERIFY_ZP(szp); | ||||
* ctldir appear to have the same v_vfsp. | |||||
*/ | |||||
if (szp->z_zfsvfs != zfsvfs || zfsctl_is_node(svp)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EXDEV)); | |||||
} | |||||
/* Prevent links to .zfs/shares files */ | /* Prevent links to .zfs/shares files */ | ||||
if ((error = sa_lookup(szp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), | if ((error = sa_lookup(szp->z_sa_hdl, SA_ZPL_PARENT(zfsvfs), | ||||
&parent, sizeof (uint64_t))) != 0) { | &parent, sizeof (uint64_t))) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
if (parent == zfsvfs->z_shares_dir) { | if (parent == zfsvfs->z_shares_dir) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EPERM)); | return (SET_ERROR(EPERM)); | ||||
} | } | ||||
if (zfsvfs->z_utf8 && u8_validate(name, | if (zfsvfs->z_utf8 && u8_validate(name, | ||||
strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | strlen(name), NULL, U8_VALIDATE_ENTIRE, &error) < 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EILSEQ)); | return (SET_ERROR(EILSEQ)); | ||||
} | } | ||||
if (flags & FIGNORECASE) | |||||
zf |= ZCILOOK; | |||||
/* | /* | ||||
* We do not support links between attributes and non-attributes | * We do not support links between attributes and non-attributes | ||||
* because of the potential security risk of creating links | * because of the potential security risk of creating links | ||||
* into "normal" file space in order to circumvent restrictions | * into "normal" file space in order to circumvent restrictions | ||||
* imposed in attribute space. | * imposed in attribute space. | ||||
*/ | */ | ||||
if ((szp->z_pflags & ZFS_XATTR) != (dzp->z_pflags & ZFS_XATTR)) { | if ((szp->z_pflags & ZFS_XATTR) != (dzp->z_pflags & ZFS_XATTR)) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EINVAL)); | return (SET_ERROR(EINVAL)); | ||||
} | } | ||||
owner = zfs_fuid_map_id(zfsvfs, szp->z_uid, cr, ZFS_OWNER); | owner = zfs_fuid_map_id(zfsvfs, szp->z_uid, cr, ZFS_OWNER); | ||||
if (owner != crgetuid(cr) && secpolicy_basic_link(svp, cr) != 0) { | if (owner != crgetuid(cr) && secpolicy_basic_link(svp, cr) != 0) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (SET_ERROR(EPERM)); | return (SET_ERROR(EPERM)); | ||||
} | } | ||||
if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
top: | |||||
/* | /* | ||||
* Attempt to lock directory; fail if entry already exists. | * Attempt to lock directory; fail if entry already exists. | ||||
*/ | */ | ||||
error = zfs_dirent_lock(&dl, dzp, name, &tzp, zf, NULL, NULL); | error = zfs_dirent_lookup(dzp, name, &tzp, ZNEW); | ||||
if (error) { | if (error) { | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
tx = dmu_tx_create(zfsvfs->z_os); | tx = dmu_tx_create(zfsvfs->z_os); | ||||
dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE); | ||||
dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | dmu_tx_hold_zap(tx, dzp->z_id, TRUE, name); | ||||
zfs_sa_upgrade_txholds(tx, szp); | zfs_sa_upgrade_txholds(tx, szp); | ||||
zfs_sa_upgrade_txholds(tx, dzp); | zfs_sa_upgrade_txholds(tx, dzp); | ||||
error = dmu_tx_assign(tx, waited ? TXG_WAITED : TXG_NOWAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
zfs_dirent_unlock(dl); | |||||
if (error == ERESTART) { | |||||
waited = B_TRUE; | |||||
dmu_tx_wait(tx); | |||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
goto top; | |||||
} | |||||
dmu_tx_abort(tx); | |||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
error = zfs_link_create(dl, szp, tx, 0); | error = zfs_link_create(dzp, name, szp, tx, 0); | ||||
if (error == 0) { | if (error == 0) { | ||||
uint64_t txtype = TX_LINK; | uint64_t txtype = TX_LINK; | ||||
if (flags & FIGNORECASE) | |||||
txtype |= TX_CI; | |||||
zfs_log_link(zilog, tx, txtype, dzp, szp, name); | zfs_log_link(zilog, tx, txtype, dzp, szp, name); | ||||
} | } | ||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
zfs_dirent_unlock(dl); | |||||
if (error == 0) { | if (error == 0) { | ||||
vnevent_link(svp, ct); | vnevent_link(svp, ct); | ||||
} | } | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef illumos | |||||
/* | |||||
* zfs_null_putapage() is used when the file system has been force | |||||
* unmounted. It just drops the pages. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_null_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, | |||||
size_t *lenp, int flags, cred_t *cr) | |||||
{ | |||||
pvn_write_done(pp, B_INVAL|B_FORCE|B_ERROR); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Push a page out to disk, klustering if possible. | |||||
* | |||||
* IN: vp - file to push page to. | |||||
* pp - page to push. | |||||
* flags - additional flags. | |||||
* cr - credentials of caller. | |||||
* | |||||
* OUT: offp - start of range pushed. | |||||
* lenp - len of range pushed. | |||||
* | |||||
* RETURN: 0 on success, error code on failure. | |||||
* | |||||
* NOTE: callers must have locked the page to be pushed. On | |||||
* exit, the page (and all other pages in the kluster) must be | |||||
* unlocked. | |||||
*/ | |||||
/* ARGSUSED */ | /*ARGSUSED*/ | ||||
static int | |||||
zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, | |||||
size_t *lenp, int flags, cred_t *cr) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
dmu_tx_t *tx; | |||||
u_offset_t off, koff; | |||||
size_t len, klen; | |||||
int err; | |||||
off = pp->p_offset; | |||||
len = PAGESIZE; | |||||
/* | |||||
* If our blocksize is bigger than the page size, try to kluster | |||||
* multiple pages so that we write a full block (thus avoiding | |||||
* a read-modify-write). | |||||
*/ | |||||
if (off < zp->z_size && zp->z_blksz > PAGESIZE) { | |||||
klen = P2ROUNDUP((ulong_t)zp->z_blksz, PAGESIZE); | |||||
koff = ISP2(klen) ? P2ALIGN(off, (u_offset_t)klen) : 0; | |||||
ASSERT(koff <= zp->z_size); | |||||
if (koff + klen > zp->z_size) | |||||
klen = P2ROUNDUP(zp->z_size - koff, (uint64_t)PAGESIZE); | |||||
pp = pvn_write_kluster(vp, pp, &off, &len, koff, klen, flags); | |||||
} | |||||
ASSERT3U(btop(len), ==, btopr(len)); | |||||
/* | |||||
* Can't push pages past end-of-file. | |||||
*/ | |||||
if (off >= zp->z_size) { | |||||
/* ignore all pages */ | |||||
err = 0; | |||||
goto out; | |||||
} else if (off + len > zp->z_size) { | |||||
int npages = btopr(zp->z_size - off); | |||||
page_t *trunc; | |||||
page_list_break(&pp, &trunc, npages); | |||||
/* ignore pages past end of file */ | |||||
if (trunc) | |||||
pvn_write_done(trunc, flags); | |||||
len = zp->z_size - off; | |||||
} | |||||
if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) || | |||||
zfs_owner_overquota(zfsvfs, zp, B_TRUE)) { | |||||
err = SET_ERROR(EDQUOT); | |||||
goto out; | |||||
} | |||||
tx = dmu_tx_create(zfsvfs->z_os); | |||||
dmu_tx_hold_write(tx, zp->z_id, off, len); | |||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | |||||
zfs_sa_upgrade_txholds(tx, zp); | |||||
err = dmu_tx_assign(tx, TXG_WAIT); | |||||
if (err != 0) { | |||||
dmu_tx_abort(tx); | |||||
goto out; | |||||
} | |||||
if (zp->z_blksz <= PAGESIZE) { | |||||
caddr_t va = zfs_map_page(pp, S_READ); | |||||
ASSERT3U(len, <=, PAGESIZE); | |||||
dmu_write(zfsvfs->z_os, zp->z_id, off, len, va, tx); | |||||
zfs_unmap_page(pp, va); | |||||
} else { | |||||
err = dmu_write_pages(zfsvfs->z_os, zp->z_id, off, len, pp, tx); | |||||
} | |||||
if (err == 0) { | |||||
uint64_t mtime[2], ctime[2]; | |||||
sa_bulk_attr_t bulk[3]; | |||||
int count = 0; | |||||
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, | |||||
&mtime, 16); | |||||
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, | |||||
&ctime, 16); | |||||
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, | |||||
&zp->z_pflags, 8); | |||||
zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime, | |||||
B_TRUE); | |||||
zfs_log_write(zfsvfs->z_log, tx, TX_WRITE, zp, off, len, 0); | |||||
} | |||||
dmu_tx_commit(tx); | |||||
out: | |||||
pvn_write_done(pp, (err ? B_ERROR : 0) | flags); | |||||
if (offp) | |||||
*offp = off; | |||||
if (lenp) | |||||
*lenp = len; | |||||
return (err); | |||||
} | |||||
/* | |||||
* Copy the portion of the file indicated from pages into the file. | |||||
* The pages are stored in a page list attached to the files vnode. | |||||
* | |||||
* IN: vp - vnode of file to push page data to. | |||||
* off - position in file to put data. | |||||
* len - amount of data to write. | |||||
* flags - flags to control the operation. | |||||
* cr - credentials of caller. | |||||
* ct - caller context. | |||||
* | |||||
* RETURN: 0 on success, error code on failure. | |||||
* | |||||
* Timestamps: | |||||
* vp - ctime|mtime updated | |||||
*/ | |||||
/*ARGSUSED*/ | |||||
static int | |||||
zfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr, | |||||
caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
page_t *pp; | |||||
size_t io_len; | |||||
u_offset_t io_off; | |||||
uint_t blksz; | |||||
rl_t *rl; | |||||
int error = 0; | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
/* | |||||
* Align this request to the file block size in case we kluster. | |||||
* XXX - this can result in pretty aggresive locking, which can | |||||
* impact simultanious read/write access. One option might be | |||||
* to break up long requests (len == 0) into block-by-block | |||||
* operations to get narrower locking. | |||||
*/ | |||||
blksz = zp->z_blksz; | |||||
if (ISP2(blksz)) | |||||
io_off = P2ALIGN_TYPED(off, blksz, u_offset_t); | |||||
else | |||||
io_off = 0; | |||||
if (len > 0 && ISP2(blksz)) | |||||
io_len = P2ROUNDUP_TYPED(len + (off - io_off), blksz, size_t); | |||||
else | |||||
io_len = 0; | |||||
if (io_len == 0) { | |||||
/* | |||||
* Search the entire vp list for pages >= io_off. | |||||
*/ | |||||
rl = zfs_range_lock(zp, io_off, UINT64_MAX, RL_WRITER); | |||||
error = pvn_vplist_dirty(vp, io_off, zfs_putapage, flags, cr); | |||||
goto out; | |||||
} | |||||
rl = zfs_range_lock(zp, io_off, io_len, RL_WRITER); | |||||
if (off > zp->z_size) { | |||||
/* past end of file */ | |||||
zfs_range_unlock(rl); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (0); | |||||
} | |||||
len = MIN(io_len, P2ROUNDUP(zp->z_size, PAGESIZE) - io_off); | |||||
for (off = io_off; io_off < off + len; io_off += io_len) { | |||||
if ((flags & B_INVAL) || ((flags & B_ASYNC) == 0)) { | |||||
pp = page_lookup(vp, io_off, | |||||
(flags & (B_INVAL | B_FREE)) ? SE_EXCL : SE_SHARED); | |||||
} else { | |||||
pp = page_lookup_nowait(vp, io_off, | |||||
(flags & B_FREE) ? SE_EXCL : SE_SHARED); | |||||
} | |||||
if (pp != NULL && pvn_getdirty(pp, flags)) { | |||||
int err; | |||||
/* | |||||
* Found a dirty page to push | |||||
*/ | |||||
err = zfs_putapage(vp, pp, &io_off, &io_len, flags, cr); | |||||
if (err) | |||||
error = err; | |||||
} else { | |||||
io_len = PAGESIZE; | |||||
} | |||||
} | |||||
out: | |||||
zfs_range_unlock(rl); | |||||
if ((flags & B_ASYNC) == 0 || zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | |||||
zil_commit(zfsvfs->z_log, zp->z_id); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
#endif /* illumos */ | |||||
/*ARGSUSED*/ | |||||
void | void | ||||
zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) | zfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct) | ||||
{ | { | ||||
znode_t *zp = VTOZ(vp); | znode_t *zp = VTOZ(vp); | ||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | zfsvfs_t *zfsvfs = zp->z_zfsvfs; | ||||
int error; | int error; | ||||
rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER); | rw_enter(&zfsvfs->z_teardown_inactive_lock, RW_READER); | ||||
if (zp->z_sa_hdl == NULL) { | if (zp->z_sa_hdl == NULL) { | ||||
/* | /* | ||||
* The fs has been unmounted, or we did a | * The fs has been unmounted, or we did a | ||||
* suspend/resume and this file no longer exists. | * suspend/resume and this file no longer exists. | ||||
*/ | */ | ||||
rw_exit(&zfsvfs->z_teardown_inactive_lock); | rw_exit(&zfsvfs->z_teardown_inactive_lock); | ||||
vrecycle(vp); | vrecycle(vp); | ||||
return; | return; | ||||
} | } | ||||
mutex_enter(&zp->z_lock); | |||||
if (zp->z_unlinked) { | if (zp->z_unlinked) { | ||||
/* | /* | ||||
* Fast path to recycle a vnode of a removed file. | * Fast path to recycle a vnode of a removed file. | ||||
*/ | */ | ||||
mutex_exit(&zp->z_lock); | |||||
rw_exit(&zfsvfs->z_teardown_inactive_lock); | rw_exit(&zfsvfs->z_teardown_inactive_lock); | ||||
vrecycle(vp); | vrecycle(vp); | ||||
return; | return; | ||||
} | } | ||||
mutex_exit(&zp->z_lock); | |||||
if (zp->z_atime_dirty && zp->z_unlinked == 0) { | if (zp->z_atime_dirty && zp->z_unlinked == 0) { | ||||
dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os); | dmu_tx_t *tx = dmu_tx_create(zfsvfs->z_os); | ||||
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE); | ||||
zfs_sa_upgrade_txholds(tx, zp); | zfs_sa_upgrade_txholds(tx, zp); | ||||
error = dmu_tx_assign(tx, TXG_WAIT); | error = dmu_tx_assign(tx, TXG_WAIT); | ||||
if (error) { | if (error) { | ||||
dmu_tx_abort(tx); | dmu_tx_abort(tx); | ||||
} else { | } else { | ||||
mutex_enter(&zp->z_lock); | |||||
(void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs), | (void) sa_update(zp->z_sa_hdl, SA_ZPL_ATIME(zfsvfs), | ||||
(void *)&zp->z_atime, sizeof (zp->z_atime), tx); | (void *)&zp->z_atime, sizeof (zp->z_atime), tx); | ||||
zp->z_atime_dirty = 0; | zp->z_atime_dirty = 0; | ||||
mutex_exit(&zp->z_lock); | |||||
dmu_tx_commit(tx); | dmu_tx_commit(tx); | ||||
} | } | ||||
} | } | ||||
rw_exit(&zfsvfs->z_teardown_inactive_lock); | rw_exit(&zfsvfs->z_teardown_inactive_lock); | ||||
} | } | ||||
#ifdef illumos | |||||
/* | |||||
* Bounds-check the seek operation. | |||||
* | |||||
* IN: vp - vnode seeking within | |||||
* ooff - old file offset | |||||
* noffp - pointer to new file offset | |||||
* ct - caller context | |||||
* | |||||
* RETURN: 0 on success, EINVAL if new offset invalid. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, | |||||
caller_context_t *ct) | |||||
{ | |||||
if (vp->v_type == VDIR) | |||||
return (0); | |||||
return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); | |||||
} | |||||
/* | |||||
* Pre-filter the generic locking function to trap attempts to place | |||||
* a mandatory lock on a memory mapped file. | |||||
*/ | |||||
static int | |||||
zfs_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset, | |||||
flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
/* | |||||
* We are following the UFS semantics with respect to mapcnt | |||||
* here: If we see that the file is mapped already, then we will | |||||
* return an error, but we don't worry about races between this | |||||
* function and zfs_map(). | |||||
*/ | |||||
if (zp->z_mapcnt > 0 && MANDMODE(zp->z_mode)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EAGAIN)); | |||||
} | |||||
ZFS_EXIT(zfsvfs); | |||||
return (fs_frlock(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct)); | |||||
} | |||||
/* | |||||
* If we can't find a page in the cache, we will create a new page | |||||
* and fill it with file data. For efficiency, we may try to fill | |||||
* multiple pages at once (klustering) to fill up the supplied page | |||||
* list. Note that the pages to be filled are held with an exclusive | |||||
* lock to prevent access by other threads while they are being filled. | |||||
*/ | |||||
static int | |||||
zfs_fillpage(vnode_t *vp, u_offset_t off, struct seg *seg, | |||||
caddr_t addr, page_t *pl[], size_t plsz, enum seg_rw rw) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
page_t *pp, *cur_pp; | |||||
objset_t *os = zp->z_zfsvfs->z_os; | |||||
u_offset_t io_off, total; | |||||
size_t io_len; | |||||
int err; | |||||
if (plsz == PAGESIZE || zp->z_blksz <= PAGESIZE) { | |||||
/* | |||||
* We only have a single page, don't bother klustering | |||||
*/ | |||||
io_off = off; | |||||
io_len = PAGESIZE; | |||||
pp = page_create_va(vp, io_off, io_len, | |||||
PG_EXCL | PG_WAIT, seg, addr); | |||||
} else { | |||||
/* | |||||
* Try to find enough pages to fill the page list | |||||
*/ | |||||
pp = pvn_read_kluster(vp, off, seg, addr, &io_off, | |||||
&io_len, off, plsz, 0); | |||||
} | |||||
if (pp == NULL) { | |||||
/* | |||||
* The page already exists, nothing to do here. | |||||
*/ | |||||
*pl = NULL; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Fill the pages in the kluster. | |||||
*/ | |||||
cur_pp = pp; | |||||
for (total = io_off + io_len; io_off < total; io_off += PAGESIZE) { | |||||
caddr_t va; | |||||
ASSERT3U(io_off, ==, cur_pp->p_offset); | |||||
va = zfs_map_page(cur_pp, S_WRITE); | |||||
err = dmu_read(os, zp->z_id, io_off, PAGESIZE, va, | |||||
DMU_READ_PREFETCH); | |||||
zfs_unmap_page(cur_pp, va); | |||||
if (err) { | |||||
/* On error, toss the entire kluster */ | |||||
pvn_read_done(pp, B_ERROR); | |||||
/* convert checksum errors into IO errors */ | |||||
if (err == ECKSUM) | |||||
err = SET_ERROR(EIO); | |||||
return (err); | |||||
} | |||||
cur_pp = cur_pp->p_next; | |||||
} | |||||
/* | |||||
* Fill in the page list array from the kluster starting | |||||
* from the desired offset `off'. | |||||
* NOTE: the page list will always be null terminated. | |||||
*/ | |||||
pvn_plist_init(pp, pl, plsz, off, io_len, rw); | |||||
ASSERT(pl == NULL || (*pl)->p_offset == off); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Return pointers to the pages for the file region [off, off + len] | |||||
* in the pl array. If plsz is greater than len, this function may | |||||
* also return page pointers from after the specified region | |||||
* (i.e. the region [off, off + plsz]). These additional pages are | |||||
* only returned if they are already in the cache, or were created as | |||||
* part of a klustered read. | |||||
* | |||||
* IN: vp - vnode of file to get data from. | |||||
* off - position in file to get data from. | |||||
* len - amount of data to retrieve. | |||||
* plsz - length of provided page list. | |||||
* seg - segment to obtain pages for. | |||||
* addr - virtual address of fault. | |||||
* rw - mode of created pages. | |||||
* cr - credentials of caller. | |||||
* ct - caller context. | |||||
* | |||||
* OUT: protp - protection mode of created pages. | |||||
* pl - list of pages created. | |||||
* | |||||
* RETURN: 0 on success, error code on failure. | |||||
* | |||||
* Timestamps: | |||||
* vp - atime updated | |||||
*/ | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp, | |||||
page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, | |||||
enum seg_rw rw, cred_t *cr, caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
page_t **pl0 = pl; | |||||
int err = 0; | |||||
/* we do our own caching, faultahead is unnecessary */ | |||||
if (pl == NULL) | |||||
return (0); | |||||
else if (len > plsz) | |||||
len = plsz; | |||||
else | |||||
len = P2ROUNDUP(len, PAGESIZE); | |||||
ASSERT(plsz >= len); | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
if (protp) | |||||
*protp = PROT_ALL; | |||||
/* | |||||
* Loop through the requested range [off, off + len) looking | |||||
* for pages. If we don't find a page, we will need to create | |||||
* a new page and fill it with data from the file. | |||||
*/ | |||||
while (len > 0) { | |||||
if (*pl = page_lookup(vp, off, SE_SHARED)) | |||||
*(pl+1) = NULL; | |||||
else if (err = zfs_fillpage(vp, off, seg, addr, pl, plsz, rw)) | |||||
goto out; | |||||
while (*pl) { | |||||
ASSERT3U((*pl)->p_offset, ==, off); | |||||
off += PAGESIZE; | |||||
addr += PAGESIZE; | |||||
if (len > 0) { | |||||
ASSERT3U(len, >=, PAGESIZE); | |||||
len -= PAGESIZE; | |||||
} | |||||
ASSERT3U(plsz, >=, PAGESIZE); | |||||
plsz -= PAGESIZE; | |||||
pl++; | |||||
} | |||||
} | |||||
/* | |||||
* Fill out the page array with any pages already in the cache. | |||||
*/ | |||||
while (plsz > 0 && | |||||
(*pl++ = page_lookup_nowait(vp, off, SE_SHARED))) { | |||||
off += PAGESIZE; | |||||
plsz -= PAGESIZE; | |||||
} | |||||
out: | |||||
if (err) { | |||||
/* | |||||
* Release any pages we have previously locked. | |||||
*/ | |||||
while (pl > pl0) | |||||
page_unlock(*--pl); | |||||
} else { | |||||
ZFS_ACCESSTIME_STAMP(zfsvfs, zp); | |||||
} | |||||
*pl = NULL; | |||||
ZFS_EXIT(zfsvfs); | |||||
return (err); | |||||
} | |||||
/* | |||||
* Request a memory map for a section of a file. This code interacts | |||||
* with common code and the VM system as follows: | |||||
* | |||||
* - common code calls mmap(), which ends up in smmap_common() | |||||
* - this calls VOP_MAP(), which takes you into (say) zfs | |||||
* - zfs_map() calls as_map(), passing segvn_create() as the callback | |||||
* - segvn_create() creates the new segment and calls VOP_ADDMAP() | |||||
* - zfs_addmap() updates z_mapcnt | |||||
*/ | |||||
/*ARGSUSED*/ | |||||
static int | |||||
zfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, | |||||
size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, | |||||
caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
segvn_crargs_t vn_a; | |||||
int error; | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
if ((prot & PROT_WRITE) && (zp->z_pflags & | |||||
(ZFS_IMMUTABLE | ZFS_READONLY | ZFS_APPENDONLY))) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EPERM)); | |||||
} | |||||
if ((prot & (PROT_READ | PROT_EXEC)) && | |||||
(zp->z_pflags & ZFS_AV_QUARANTINED)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EACCES)); | |||||
} | |||||
if (vp->v_flag & VNOMAP) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(ENOSYS)); | |||||
} | |||||
if (off < 0 || len > MAXOFFSET_T - off) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(ENXIO)); | |||||
} | |||||
if (vp->v_type != VREG) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(ENODEV)); | |||||
} | |||||
/* | |||||
* If file is locked, disallow mapping. | |||||
*/ | |||||
if (MANDMODE(zp->z_mode) && vn_has_flocks(vp)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EAGAIN)); | |||||
} | |||||
as_rangelock(as); | |||||
error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags); | |||||
if (error != 0) { | |||||
as_rangeunlock(as); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
vn_a.vp = vp; | |||||
vn_a.offset = (u_offset_t)off; | |||||
vn_a.type = flags & MAP_TYPE; | |||||
vn_a.prot = prot; | |||||
vn_a.maxprot = maxprot; | |||||
vn_a.cred = cr; | |||||
vn_a.amp = NULL; | |||||
vn_a.flags = flags & ~MAP_TYPE; | |||||
vn_a.szc = 0; | |||||
vn_a.lgrp_mem_policy_flags = 0; | |||||
error = as_map(as, *addrp, len, segvn_create, &vn_a); | |||||
as_rangeunlock(as); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, | |||||
size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr, | |||||
caller_context_t *ct) | |||||
{ | |||||
uint64_t pages = btopr(len); | |||||
atomic_add_64(&VTOZ(vp)->z_mapcnt, pages); | |||||
return (0); | |||||
} | |||||
/* | |||||
* The reason we push dirty pages as part of zfs_delmap() is so that we get a | |||||
* more accurate mtime for the associated file. Since we don't have a way of | |||||
* detecting when the data was actually modified, we have to resort to | |||||
* heuristics. If an explicit msync() is done, then we mark the mtime when the | |||||
* last page is pushed. The problem occurs when the msync() call is omitted, | |||||
* which by far the most common case: | |||||
* | |||||
* open() | |||||
* mmap() | |||||
* <modify memory> | |||||
* munmap() | |||||
* close() | |||||
* <time lapse> | |||||
* putpage() via fsflush | |||||
* | |||||
* If we wait until fsflush to come along, we can have a modification time that | |||||
* is some arbitrary point in the future. In order to prevent this in the | |||||
* common case, we flush pages whenever a (MAP_SHARED, PROT_WRITE) mapping is | |||||
* torn down. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, | |||||
size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr, | |||||
caller_context_t *ct) | |||||
{ | |||||
uint64_t pages = btopr(len); | |||||
ASSERT3U(VTOZ(vp)->z_mapcnt, >=, pages); | |||||
atomic_add_64(&VTOZ(vp)->z_mapcnt, -pages); | |||||
if ((flags & MAP_SHARED) && (prot & PROT_WRITE) && | |||||
vn_has_cached_data(vp)) | |||||
(void) VOP_PUTPAGE(vp, off, len, B_ASYNC, cr, ct); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Free or allocate space in a file. Currently, this function only | |||||
* supports the `F_FREESP' command. However, this command is somewhat | |||||
* misnamed, as its functionality includes the ability to allocate as | |||||
* well as free space. | |||||
* | |||||
* IN: vp - vnode of file to free data in. | |||||
* cmd - action to take (only F_FREESP supported). | |||||
* bfp - section of file to free/alloc. | |||||
* flag - current file open mode flags. | |||||
* offset - current file offset. | |||||
* cr - credentials of caller [UNUSED]. | |||||
* ct - caller context. | |||||
* | |||||
* RETURN: 0 on success, error code on failure. | |||||
* | |||||
* Timestamps: | |||||
* vp - ctime|mtime updated | |||||
*/ | |||||
/* ARGSUSED */ | |||||
static int | |||||
zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, | |||||
offset_t offset, cred_t *cr, caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
uint64_t off, len; | |||||
int error; | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
if (cmd != F_FREESP) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
/* | |||||
* In a case vp->v_vfsp != zp->z_zfsvfs->z_vfs (e.g. snapshots) our | |||||
* callers might not be able to detect properly that we are read-only, | |||||
* so check it explicitly here. | |||||
*/ | |||||
if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EROFS)); | |||||
} | |||||
if (error = convoff(vp, bfp, 0, offset)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
if (bfp->l_len < 0) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
off = bfp->l_start; | |||||
len = bfp->l_len; /* 0 means from off to end of file */ | |||||
error = zfs_freesp(zp, off, len, flag, TRUE); | |||||
ZFS_EXIT(zfsvfs); | |||||
return (error); | |||||
} | |||||
#endif /* illumos */ | |||||
CTASSERT(sizeof(struct zfid_short) <= sizeof(struct fid)); | CTASSERT(sizeof(struct zfid_short) <= sizeof(struct fid)); | ||||
CTASSERT(sizeof(struct zfid_long) <= sizeof(struct fid)); | CTASSERT(sizeof(struct zfid_long) <= sizeof(struct fid)); | ||||
/*ARGSUSED*/ | /*ARGSUSED*/ | ||||
static int | static int | ||||
zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) | zfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct) | ||||
{ | { | ||||
znode_t *zp = VTOZ(vp); | znode_t *zp = VTOZ(vp); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, | zfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, | ||||
caller_context_t *ct) | caller_context_t *ct) | ||||
{ | { | ||||
znode_t *zp, *xzp; | znode_t *zp, *xzp; | ||||
zfsvfs_t *zfsvfs; | zfsvfs_t *zfsvfs; | ||||
zfs_dirlock_t *dl; | |||||
int error; | int error; | ||||
switch (cmd) { | switch (cmd) { | ||||
case _PC_LINK_MAX: | case _PC_LINK_MAX: | ||||
*valp = INT_MAX; | *valp = INT_MAX; | ||||
return (0); | return (0); | ||||
case _PC_FILESIZEBITS: | case _PC_FILESIZEBITS: | ||||
*valp = 64; | *valp = 64; | ||||
return (0); | return (0); | ||||
#ifdef illumos | #ifdef illumos | ||||
case _PC_XATTR_EXISTS: | case _PC_XATTR_EXISTS: | ||||
zp = VTOZ(vp); | zp = VTOZ(vp); | ||||
zfsvfs = zp->z_zfsvfs; | zfsvfs = zp->z_zfsvfs; | ||||
ZFS_ENTER(zfsvfs); | ZFS_ENTER(zfsvfs); | ||||
ZFS_VERIFY_ZP(zp); | ZFS_VERIFY_ZP(zp); | ||||
*valp = 0; | *valp = 0; | ||||
error = zfs_dirent_lock(&dl, zp, "", &xzp, | error = zfs_dirent_lookup(zp, "", &xzp, | ||||
ZXATTR | ZEXISTS | ZSHARED, NULL, NULL); | ZXATTR | ZEXISTS | ZSHARED); | ||||
if (error == 0) { | if (error == 0) { | ||||
zfs_dirent_unlock(dl); | |||||
if (!zfs_dirempty(xzp)) | if (!zfs_dirempty(xzp)) | ||||
*valp = 1; | *valp = 1; | ||||
VN_RELE(ZTOV(xzp)); | vrele(ZTOV(xzp)); | ||||
} else if (error == ENOENT) { | } else if (error == ENOENT) { | ||||
/* | /* | ||||
* If there aren't extended attributes, it's the | * If there aren't extended attributes, it's the | ||||
* same as having zero of them. | * same as having zero of them. | ||||
*/ | */ | ||||
error = 0; | error = 0; | ||||
} | } | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr, | ||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) | ||||
zil_commit(zilog, 0); | zil_commit(zilog, 0); | ||||
ZFS_EXIT(zfsvfs); | ZFS_EXIT(zfsvfs); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef illumos | |||||
/* | |||||
* The smallest read we may consider to loan out an arcbuf. | |||||
* This must be a power of 2. | |||||
*/ | |||||
int zcr_blksz_min = (1 << 10); /* 1K */ | |||||
/* | |||||
* If set to less than the file block size, allow loaning out of an | |||||
* arcbuf for a partial block read. This must be a power of 2. | |||||
*/ | |||||
int zcr_blksz_max = (1 << 17); /* 128K */ | |||||
/*ARGSUSED*/ | |||||
static int | static int | ||||
zfs_reqzcbuf(vnode_t *vp, enum uio_rw ioflag, xuio_t *xuio, cred_t *cr, | |||||
caller_context_t *ct) | |||||
{ | |||||
znode_t *zp = VTOZ(vp); | |||||
zfsvfs_t *zfsvfs = zp->z_zfsvfs; | |||||
int max_blksz = zfsvfs->z_max_blksz; | |||||
uio_t *uio = &xuio->xu_uio; | |||||
ssize_t size = uio->uio_resid; | |||||
offset_t offset = uio->uio_loffset; | |||||
int blksz; | |||||
int fullblk, i; | |||||
arc_buf_t *abuf; | |||||
ssize_t maxsize; | |||||
int preamble, postamble; | |||||
if (xuio->xu_type != UIOTYPE_ZEROCOPY) | |||||
return (SET_ERROR(EINVAL)); | |||||
ZFS_ENTER(zfsvfs); | |||||
ZFS_VERIFY_ZP(zp); | |||||
switch (ioflag) { | |||||
case UIO_WRITE: | |||||
/* | |||||
* Loan out an arc_buf for write if write size is bigger than | |||||
* max_blksz, and the file's block size is also max_blksz. | |||||
*/ | |||||
blksz = max_blksz; | |||||
if (size < blksz || zp->z_blksz != blksz) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
/* | |||||
* Caller requests buffers for write before knowing where the | |||||
* write offset might be (e.g. NFS TCP write). | |||||
*/ | |||||
if (offset == -1) { | |||||
preamble = 0; | |||||
} else { | |||||
preamble = P2PHASE(offset, blksz); | |||||
if (preamble) { | |||||
preamble = blksz - preamble; | |||||
size -= preamble; | |||||
} | |||||
} | |||||
postamble = P2PHASE(size, blksz); | |||||
size -= postamble; | |||||
fullblk = size / blksz; | |||||
(void) dmu_xuio_init(xuio, | |||||
(preamble != 0) + fullblk + (postamble != 0)); | |||||
DTRACE_PROBE3(zfs_reqzcbuf_align, int, preamble, | |||||
int, postamble, int, | |||||
(preamble != 0) + fullblk + (postamble != 0)); | |||||
/* | |||||
* Have to fix iov base/len for partial buffers. They | |||||
* currently represent full arc_buf's. | |||||
*/ | |||||
if (preamble) { | |||||
/* data begins in the middle of the arc_buf */ | |||||
abuf = dmu_request_arcbuf(sa_get_db(zp->z_sa_hdl), | |||||
blksz); | |||||
ASSERT(abuf); | |||||
(void) dmu_xuio_add(xuio, abuf, | |||||
blksz - preamble, preamble); | |||||
} | |||||
for (i = 0; i < fullblk; i++) { | |||||
abuf = dmu_request_arcbuf(sa_get_db(zp->z_sa_hdl), | |||||
blksz); | |||||
ASSERT(abuf); | |||||
(void) dmu_xuio_add(xuio, abuf, 0, blksz); | |||||
} | |||||
if (postamble) { | |||||
/* data ends in the middle of the arc_buf */ | |||||
abuf = dmu_request_arcbuf(sa_get_db(zp->z_sa_hdl), | |||||
blksz); | |||||
ASSERT(abuf); | |||||
(void) dmu_xuio_add(xuio, abuf, 0, postamble); | |||||
} | |||||
break; | |||||
case UIO_READ: | |||||
/* | |||||
* Loan out an arc_buf for read if the read size is larger than | |||||
* the current file block size. Block alignment is not | |||||
* considered. Partial arc_buf will be loaned out for read. | |||||
*/ | |||||
blksz = zp->z_blksz; | |||||
if (blksz < zcr_blksz_min) | |||||
blksz = zcr_blksz_min; | |||||
if (blksz > zcr_blksz_max) | |||||
blksz = zcr_blksz_max; | |||||
/* avoid potential complexity of dealing with it */ | |||||
if (blksz > max_blksz) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
maxsize = zp->z_size - uio->uio_loffset; | |||||
if (size > maxsize) | |||||
size = maxsize; | |||||
if (size < blksz || vn_has_cached_data(vp)) { | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
break; | |||||
default: | |||||
ZFS_EXIT(zfsvfs); | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
uio->uio_extflg = UIO_XUIO; | |||||
XUIO_XUZC_RW(xuio) = ioflag; | |||||
ZFS_EXIT(zfsvfs); | |||||
return (0); | |||||
} | |||||
/*ARGSUSED*/ | |||||
static int | |||||
zfs_retzcbuf(vnode_t *vp, xuio_t *xuio, cred_t *cr, caller_context_t *ct) | |||||
{ | |||||
int i; | |||||
arc_buf_t *abuf; | |||||
int ioflag = XUIO_XUZC_RW(xuio); | |||||
ASSERT(xuio->xu_type == UIOTYPE_ZEROCOPY); | |||||
i = dmu_xuio_cnt(xuio); | |||||
while (i-- > 0) { | |||||
abuf = dmu_xuio_arcbuf(xuio, i); | |||||
/* | |||||
* if abuf == NULL, it must be a write buffer | |||||
* that has been returned in zfs_write(). | |||||
*/ | |||||
if (abuf) | |||||
dmu_return_arcbuf(abuf); | |||||
ASSERT(abuf || ioflag == UIO_WRITE); | |||||
} | |||||
dmu_xuio_fini(xuio); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Predeclare these here so that the compiler assumes that | |||||
* this is an "old style" function declaration that does | |||||
* not include arguments => we won't get type mismatch errors | |||||
* in the initializations that follow. | |||||
*/ | |||||
static int zfs_inval(); | |||||
static int zfs_isdir(); | |||||
static int | |||||
zfs_inval() | |||||
{ | |||||
return (SET_ERROR(EINVAL)); | |||||
} | |||||
static int | |||||
zfs_isdir() | |||||
{ | |||||
return (SET_ERROR(EISDIR)); | |||||
} | |||||
/* | |||||
* Directory vnode operations template | |||||
*/ | |||||
vnodeops_t *zfs_dvnodeops; | |||||
const fs_operation_def_t zfs_dvnodeops_template[] = { | |||||
VOPNAME_OPEN, { .vop_open = zfs_open }, | |||||
VOPNAME_CLOSE, { .vop_close = zfs_close }, | |||||
VOPNAME_READ, { .error = zfs_isdir }, | |||||
VOPNAME_WRITE, { .error = zfs_isdir }, | |||||
VOPNAME_IOCTL, { .vop_ioctl = zfs_ioctl }, | |||||
VOPNAME_GETATTR, { .vop_getattr = zfs_getattr }, | |||||
VOPNAME_SETATTR, { .vop_setattr = zfs_setattr }, | |||||
VOPNAME_ACCESS, { .vop_access = zfs_access }, | |||||
VOPNAME_LOOKUP, { .vop_lookup = zfs_lookup }, | |||||
VOPNAME_CREATE, { .vop_create = zfs_create }, | |||||
VOPNAME_REMOVE, { .vop_remove = zfs_remove }, | |||||
VOPNAME_LINK, { .vop_link = zfs_link }, | |||||
VOPNAME_RENAME, { .vop_rename = zfs_rename }, | |||||
VOPNAME_MKDIR, { .vop_mkdir = zfs_mkdir }, | |||||
VOPNAME_RMDIR, { .vop_rmdir = zfs_rmdir }, | |||||
VOPNAME_READDIR, { .vop_readdir = zfs_readdir }, | |||||
VOPNAME_SYMLINK, { .vop_symlink = zfs_symlink }, | |||||
VOPNAME_FSYNC, { .vop_fsync = zfs_fsync }, | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_FID, { .vop_fid = zfs_fid }, | |||||
VOPNAME_SEEK, { .vop_seek = zfs_seek }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
VOPNAME_GETSECATTR, { .vop_getsecattr = zfs_getsecattr }, | |||||
VOPNAME_SETSECATTR, { .vop_setsecattr = zfs_setsecattr }, | |||||
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, | |||||
NULL, NULL | |||||
}; | |||||
/* | |||||
* Regular file vnode operations template | |||||
*/ | |||||
vnodeops_t *zfs_fvnodeops; | |||||
const fs_operation_def_t zfs_fvnodeops_template[] = { | |||||
VOPNAME_OPEN, { .vop_open = zfs_open }, | |||||
VOPNAME_CLOSE, { .vop_close = zfs_close }, | |||||
VOPNAME_READ, { .vop_read = zfs_read }, | |||||
VOPNAME_WRITE, { .vop_write = zfs_write }, | |||||
VOPNAME_IOCTL, { .vop_ioctl = zfs_ioctl }, | |||||
VOPNAME_GETATTR, { .vop_getattr = zfs_getattr }, | |||||
VOPNAME_SETATTR, { .vop_setattr = zfs_setattr }, | |||||
VOPNAME_ACCESS, { .vop_access = zfs_access }, | |||||
VOPNAME_LOOKUP, { .vop_lookup = zfs_lookup }, | |||||
VOPNAME_RENAME, { .vop_rename = zfs_rename }, | |||||
VOPNAME_FSYNC, { .vop_fsync = zfs_fsync }, | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_FID, { .vop_fid = zfs_fid }, | |||||
VOPNAME_SEEK, { .vop_seek = zfs_seek }, | |||||
VOPNAME_FRLOCK, { .vop_frlock = zfs_frlock }, | |||||
VOPNAME_SPACE, { .vop_space = zfs_space }, | |||||
VOPNAME_GETPAGE, { .vop_getpage = zfs_getpage }, | |||||
VOPNAME_PUTPAGE, { .vop_putpage = zfs_putpage }, | |||||
VOPNAME_MAP, { .vop_map = zfs_map }, | |||||
VOPNAME_ADDMAP, { .vop_addmap = zfs_addmap }, | |||||
VOPNAME_DELMAP, { .vop_delmap = zfs_delmap }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
VOPNAME_GETSECATTR, { .vop_getsecattr = zfs_getsecattr }, | |||||
VOPNAME_SETSECATTR, { .vop_setsecattr = zfs_setsecattr }, | |||||
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, | |||||
VOPNAME_REQZCBUF, { .vop_reqzcbuf = zfs_reqzcbuf }, | |||||
VOPNAME_RETZCBUF, { .vop_retzcbuf = zfs_retzcbuf }, | |||||
NULL, NULL | |||||
}; | |||||
/* | |||||
* Symbolic link vnode operations template | |||||
*/ | |||||
vnodeops_t *zfs_symvnodeops; | |||||
const fs_operation_def_t zfs_symvnodeops_template[] = { | |||||
VOPNAME_GETATTR, { .vop_getattr = zfs_getattr }, | |||||
VOPNAME_SETATTR, { .vop_setattr = zfs_setattr }, | |||||
VOPNAME_ACCESS, { .vop_access = zfs_access }, | |||||
VOPNAME_RENAME, { .vop_rename = zfs_rename }, | |||||
VOPNAME_READLINK, { .vop_readlink = zfs_readlink }, | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_FID, { .vop_fid = zfs_fid }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, | |||||
NULL, NULL | |||||
}; | |||||
/* | |||||
* special share hidden files vnode operations template | |||||
*/ | |||||
vnodeops_t *zfs_sharevnodeops; | |||||
const fs_operation_def_t zfs_sharevnodeops_template[] = { | |||||
VOPNAME_GETATTR, { .vop_getattr = zfs_getattr }, | |||||
VOPNAME_ACCESS, { .vop_access = zfs_access }, | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_FID, { .vop_fid = zfs_fid }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
VOPNAME_GETSECATTR, { .vop_getsecattr = zfs_getsecattr }, | |||||
VOPNAME_SETSECATTR, { .vop_setsecattr = zfs_setsecattr }, | |||||
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, | |||||
NULL, NULL | |||||
}; | |||||
/* | |||||
* Extended attribute directory vnode operations template | |||||
* | |||||
* This template is identical to the directory vnodes | |||||
* operation template except for restricted operations: | |||||
* VOP_MKDIR() | |||||
* VOP_SYMLINK() | |||||
* | |||||
* Note that there are other restrictions embedded in: | |||||
* zfs_create() - restrict type to VREG | |||||
* zfs_link() - no links into/out of attribute space | |||||
* zfs_rename() - no moves into/out of attribute space | |||||
*/ | |||||
vnodeops_t *zfs_xdvnodeops; | |||||
const fs_operation_def_t zfs_xdvnodeops_template[] = { | |||||
VOPNAME_OPEN, { .vop_open = zfs_open }, | |||||
VOPNAME_CLOSE, { .vop_close = zfs_close }, | |||||
VOPNAME_IOCTL, { .vop_ioctl = zfs_ioctl }, | |||||
VOPNAME_GETATTR, { .vop_getattr = zfs_getattr }, | |||||
VOPNAME_SETATTR, { .vop_setattr = zfs_setattr }, | |||||
VOPNAME_ACCESS, { .vop_access = zfs_access }, | |||||
VOPNAME_LOOKUP, { .vop_lookup = zfs_lookup }, | |||||
VOPNAME_CREATE, { .vop_create = zfs_create }, | |||||
VOPNAME_REMOVE, { .vop_remove = zfs_remove }, | |||||
VOPNAME_LINK, { .vop_link = zfs_link }, | |||||
VOPNAME_RENAME, { .vop_rename = zfs_rename }, | |||||
VOPNAME_MKDIR, { .error = zfs_inval }, | |||||
VOPNAME_RMDIR, { .vop_rmdir = zfs_rmdir }, | |||||
VOPNAME_READDIR, { .vop_readdir = zfs_readdir }, | |||||
VOPNAME_SYMLINK, { .error = zfs_inval }, | |||||
VOPNAME_FSYNC, { .vop_fsync = zfs_fsync }, | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_FID, { .vop_fid = zfs_fid }, | |||||
VOPNAME_SEEK, { .vop_seek = zfs_seek }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
VOPNAME_GETSECATTR, { .vop_getsecattr = zfs_getsecattr }, | |||||
VOPNAME_SETSECATTR, { .vop_setsecattr = zfs_setsecattr }, | |||||
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support }, | |||||
NULL, NULL | |||||
}; | |||||
/* | |||||
* Error vnode operations template | |||||
*/ | |||||
vnodeops_t *zfs_evnodeops; | |||||
const fs_operation_def_t zfs_evnodeops_template[] = { | |||||
VOPNAME_INACTIVE, { .vop_inactive = zfs_inactive }, | |||||
VOPNAME_PATHCONF, { .vop_pathconf = zfs_pathconf }, | |||||
NULL, NULL | |||||
}; | |||||
#endif /* illumos */ | |||||
static int | |||||
ioflags(int ioflags) | ioflags(int ioflags) | ||||
{ | { | ||||
int flags = 0; | int flags = 0; | ||||
if (ioflags & IO_APPEND) | if (ioflags & IO_APPEND) | ||||
flags |= FAPPEND; | flags |= FAPPEND; | ||||
if (ioflags & IO_NDELAY) | if (ioflags & IO_NDELAY) | ||||
flags |= FNONBLOCK; | flags |= FNONBLOCK; | ||||
if (ioflags & IO_SYNC) | if (ioflags & IO_SYNC) | ||||
flags |= (FSYNC | FDSYNC | FRSYNC); | flags |= (FSYNC | FDSYNC | FRSYNC); | ||||
return (flags); | return (flags); | ||||
} | } | ||||
static int | static int | ||||
zfs_getpages(struct vnode *vp, vm_page_t *m, int count, int *rbehind, | zfs_getpages(struct vnode *vp, vm_page_t *m, int count, int *rbehind, | ||||
▲ Show 20 Lines • Show All 427 Lines • ▼ Show 20 Lines | zfs_freebsd_create(ap) | ||||
ASSERT(cnp->cn_flags & SAVENAME); | ASSERT(cnp->cn_flags & SAVENAME); | ||||
vattr_init_mask(vap); | vattr_init_mask(vap); | ||||
mode = vap->va_mode & ALLPERMS; | mode = vap->va_mode & ALLPERMS; | ||||
error = zfs_create(ap->a_dvp, cnp->cn_nameptr, vap, !EXCL, mode, | error = zfs_create(ap->a_dvp, cnp->cn_nameptr, vap, !EXCL, mode, | ||||
ap->a_vpp, cnp->cn_cred, cnp->cn_thread); | ap->a_vpp, cnp->cn_cred, cnp->cn_thread); | ||||
#ifdef FREEBSD_NAMECACHE | |||||
if (error == 0 && (cnp->cn_flags & MAKEENTRY) != 0) | if (error == 0 && (cnp->cn_flags & MAKEENTRY) != 0) | ||||
cache_enter(ap->a_dvp, *ap->a_vpp, cnp); | cache_enter(ap->a_dvp, *ap->a_vpp, cnp); | ||||
#endif | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
zfs_freebsd_remove(ap) | zfs_freebsd_remove(ap) | ||||
struct vop_remove_args /* { | struct vop_remove_args /* { | ||||
struct vnode *a_dvp; | struct vnode *a_dvp; | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
struct componentname *a_cnp; | struct componentname *a_cnp; | ||||
} */ *ap; | } */ *ap; | ||||
{ | { | ||||
ASSERT(ap->a_cnp->cn_flags & SAVENAME); | ASSERT(ap->a_cnp->cn_flags & SAVENAME); | ||||
return (zfs_remove(ap->a_dvp, ap->a_cnp->cn_nameptr, | return (zfs_remove(ap->a_dvp, ap->a_vp, ap->a_cnp->cn_nameptr, | ||||
ap->a_cnp->cn_cred, NULL, 0)); | ap->a_cnp->cn_cred)); | ||||
} | } | ||||
static int | static int | ||||
zfs_freebsd_mkdir(ap) | zfs_freebsd_mkdir(ap) | ||||
struct vop_mkdir_args /* { | struct vop_mkdir_args /* { | ||||
struct vnode *a_dvp; | struct vnode *a_dvp; | ||||
struct vnode **a_vpp; | struct vnode **a_vpp; | ||||
struct componentname *a_cnp; | struct componentname *a_cnp; | ||||
struct vattr *a_vap; | struct vattr *a_vap; | ||||
} */ *ap; | } */ *ap; | ||||
{ | { | ||||
vattr_t *vap = ap->a_vap; | vattr_t *vap = ap->a_vap; | ||||
ASSERT(ap->a_cnp->cn_flags & SAVENAME); | ASSERT(ap->a_cnp->cn_flags & SAVENAME); | ||||
vattr_init_mask(vap); | vattr_init_mask(vap); | ||||
return (zfs_mkdir(ap->a_dvp, ap->a_cnp->cn_nameptr, vap, ap->a_vpp, | return (zfs_mkdir(ap->a_dvp, ap->a_cnp->cn_nameptr, vap, ap->a_vpp, | ||||
ap->a_cnp->cn_cred, NULL, 0, NULL)); | ap->a_cnp->cn_cred)); | ||||
} | } | ||||
static int | static int | ||||
zfs_freebsd_rmdir(ap) | zfs_freebsd_rmdir(ap) | ||||
struct vop_rmdir_args /* { | struct vop_rmdir_args /* { | ||||
struct vnode *a_dvp; | struct vnode *a_dvp; | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
struct componentname *a_cnp; | struct componentname *a_cnp; | ||||
} */ *ap; | } */ *ap; | ||||
{ | { | ||||
struct componentname *cnp = ap->a_cnp; | struct componentname *cnp = ap->a_cnp; | ||||
ASSERT(cnp->cn_flags & SAVENAME); | ASSERT(cnp->cn_flags & SAVENAME); | ||||
return (zfs_rmdir(ap->a_dvp, cnp->cn_nameptr, NULL, cnp->cn_cred, NULL, 0)); | return (zfs_rmdir(ap->a_dvp, ap->a_vp, cnp->cn_nameptr, cnp->cn_cred)); | ||||
} | } | ||||
static int | static int | ||||
zfs_freebsd_readdir(ap) | zfs_freebsd_readdir(ap) | ||||
struct vop_readdir_args /* { | struct vop_readdir_args /* { | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
struct uio *a_uio; | struct uio *a_uio; | ||||
struct ucred *a_cred; | struct ucred *a_cred; | ||||
▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | zfs_freebsd_rename(ap) | ||||
vnode_t *fvp = ap->a_fvp; | vnode_t *fvp = ap->a_fvp; | ||||
vnode_t *tdvp = ap->a_tdvp; | vnode_t *tdvp = ap->a_tdvp; | ||||
vnode_t *tvp = ap->a_tvp; | vnode_t *tvp = ap->a_tvp; | ||||
int error; | int error; | ||||
ASSERT(ap->a_fcnp->cn_flags & (SAVENAME|SAVESTART)); | ASSERT(ap->a_fcnp->cn_flags & (SAVENAME|SAVESTART)); | ||||
ASSERT(ap->a_tcnp->cn_flags & (SAVENAME|SAVESTART)); | ASSERT(ap->a_tcnp->cn_flags & (SAVENAME|SAVESTART)); | ||||
/* | error = zfs_rename(fdvp, &fvp, ap->a_fcnp, tdvp, &tvp, | ||||
* Check for cross-device rename. | ap->a_tcnp, ap->a_fcnp->cn_cred); | ||||
*/ | |||||
if ((fdvp->v_mount != tdvp->v_mount) || | |||||
(tvp && (fdvp->v_mount != tvp->v_mount))) | |||||
error = EXDEV; | |||||
else | |||||
error = zfs_rename(fdvp, ap->a_fcnp->cn_nameptr, tdvp, | |||||
ap->a_tcnp->cn_nameptr, ap->a_fcnp->cn_cred, NULL, 0); | |||||
if (tdvp == tvp) | |||||
VN_RELE(tdvp); | |||||
else | |||||
VN_URELE(tdvp); | |||||
if (tvp) | |||||
VN_URELE(tvp); | |||||
VN_RELE(fdvp); | |||||
VN_RELE(fvp); | |||||
vrele(fdvp); | |||||
vrele(fvp); | |||||
vrele(tdvp); | |||||
if (tvp != NULL) | |||||
vrele(tvp); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
zfs_freebsd_symlink(ap) | zfs_freebsd_symlink(ap) | ||||
struct vop_symlink_args /* { | struct vop_symlink_args /* { | ||||
struct vnode *a_dvp; | struct vnode *a_dvp; | ||||
struct vnode **a_vpp; | struct vnode **a_vpp; | ||||
▲ Show 20 Lines • Show All 653 Lines • ▼ Show 20 Lines | |||||
struct vop_vector zfs_fifoops; | struct vop_vector zfs_fifoops; | ||||
struct vop_vector zfs_shareops; | struct vop_vector zfs_shareops; | ||||
struct vop_vector zfs_vnodeops = { | struct vop_vector zfs_vnodeops = { | ||||
.vop_default = &default_vnodeops, | .vop_default = &default_vnodeops, | ||||
.vop_inactive = zfs_freebsd_inactive, | .vop_inactive = zfs_freebsd_inactive, | ||||
.vop_reclaim = zfs_freebsd_reclaim, | .vop_reclaim = zfs_freebsd_reclaim, | ||||
.vop_access = zfs_freebsd_access, | .vop_access = zfs_freebsd_access, | ||||
#ifdef FREEBSD_NAMECACHE | |||||
.vop_lookup = vfs_cache_lookup, | .vop_lookup = vfs_cache_lookup, | ||||
.vop_cachedlookup = zfs_freebsd_lookup, | .vop_cachedlookup = zfs_freebsd_lookup, | ||||
#else | |||||
.vop_lookup = zfs_freebsd_lookup, | |||||
#endif | |||||
.vop_getattr = zfs_freebsd_getattr, | .vop_getattr = zfs_freebsd_getattr, | ||||
.vop_setattr = zfs_freebsd_setattr, | .vop_setattr = zfs_freebsd_setattr, | ||||
.vop_create = zfs_freebsd_create, | .vop_create = zfs_freebsd_create, | ||||
.vop_mknod = zfs_freebsd_create, | .vop_mknod = zfs_freebsd_create, | ||||
.vop_mkdir = zfs_freebsd_mkdir, | .vop_mkdir = zfs_freebsd_mkdir, | ||||
.vop_readdir = zfs_freebsd_readdir, | .vop_readdir = zfs_freebsd_readdir, | ||||
.vop_fsync = zfs_freebsd_fsync, | .vop_fsync = zfs_freebsd_fsync, | ||||
.vop_open = zfs_freebsd_open, | .vop_open = zfs_freebsd_open, | ||||
▲ Show 20 Lines • Show All 53 Lines • Show Last 20 Lines |