Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_syscalls.c
| Show First 20 Lines • Show All 3,780 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| struct mount *mp, *tmp; | struct mount *mp, *tmp; | ||||
| struct vnode *tvp, *fvp, *tdvp; | struct vnode *tvp, *fvp, *tdvp; | ||||
| struct nameidata fromnd, tond; | struct nameidata fromnd, tond; | ||||
| uint64_t tondflags; | uint64_t tondflags; | ||||
| int error; | int error; | ||||
| short irflag; | short irflag; | ||||
| if ((flags & ~(AT_RENAME_NOREPLACE)) != 0) | if ((flags & ~(AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) != 0) | ||||
jah: The Linux man pages I looked at seem to indicate EINVAL should be returned if both NOREPLACE… | |||||
emasteUnsubmitted Done Inline ActionsIt's probably worth mirroring the Linux behaviour here. emaste: It's probably worth mirroring the Linux behaviour here.
| |||||
| return (EINVAL); | return (EINVAL); | ||||
| again: | again: | ||||
| tmp = mp = NULL; | tmp = mp = NULL; | ||||
| bwillwrite(); | bwillwrite(); | ||||
| #ifdef MAC | #ifdef MAC | ||||
| if (mac_vnode_check_rename_from_enabled()) { | if (mac_vnode_check_rename_from_enabled()) { | ||||
| error = kern_renameat_mac(td, oldfd, old, newfd, new, pathseg, | error = kern_renameat_mac(td, oldfd, old, newfd, new, pathseg, | ||||
| &fromnd); | &fromnd); | ||||
| if (error != 0) | if (error != 0) | ||||
| return (error); | return (error); | ||||
| } else { | } else { | ||||
| #endif | #endif | ||||
| NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | AUDITVNODE1, | NDINIT_ATRIGHTS(&fromnd, (flags & AT_RENAME_EXCHANGE) != 0 ? RENAME : | ||||
| DELETE, WANTPARENT | AUDITVNODE1, | |||||
| pathseg, old, oldfd, &cap_renameat_source_rights); | pathseg, old, oldfd, &cap_renameat_source_rights); | ||||
| if ((error = namei(&fromnd)) != 0) | if ((error = namei(&fromnd)) != 0) | ||||
| return (error); | return (error); | ||||
| #ifdef MAC | #ifdef MAC | ||||
| } | } | ||||
| #endif | #endif | ||||
| fvp = fromnd.ni_vp; | fvp = fromnd.ni_vp; | ||||
| tondflags = LOCKPARENT | LOCKLEAF | NOCACHE | AUDITVNODE2; | tondflags = LOCKPARENT | LOCKLEAF | NOCACHE | AUDITVNODE2; | ||||
| if (fromnd.ni_vp->v_type == VDIR) | if (fromnd.ni_vp->v_type == VDIR) | ||||
| tondflags |= WILLBEDIR; | tondflags |= WILLBEDIR; | ||||
Done Inline ActionsDo we need to set WILLBEDIR in the other direction? markj: Do we need to set WILLBEDIR in the other direction? | |||||
| NDINIT_ATRIGHTS(&tond, RENAME, tondflags, pathseg, new, newfd, | NDINIT_ATRIGHTS(&tond, RENAME, tondflags, pathseg, new, newfd, | ||||
| &cap_renameat_target_rights); | &cap_renameat_target_rights); | ||||
| if ((error = namei(&tond)) != 0) { | if ((error = namei(&tond)) != 0) { | ||||
| /* Translate error code for rename("dir1", "dir2/."). */ | /* Translate error code for rename("dir1", "dir2/."). */ | ||||
| if (error == EISDIR && fvp->v_type == VDIR) | if (error == EISDIR && fvp->v_type == VDIR) | ||||
| error = EINVAL; | error = EINVAL; | ||||
| NDFREE_PNBUF(&fromnd); | NDFREE_PNBUF(&fromnd); | ||||
| vrele(fromnd.ni_dvp); | vrele(fromnd.ni_dvp); | ||||
| Show All 19 Lines | if (tvp != NULL && (flags & AT_RENAME_NOREPLACE) != 0) { | ||||
| * implement AT_RENAME_NOREPLACE. This leads to | * implement AT_RENAME_NOREPLACE. This leads to | ||||
| * situation where sometimes EOPNOTSUPP might be | * situation where sometimes EOPNOTSUPP might be | ||||
| * returned from the VOP due to race, while most of | * returned from the VOP due to race, while most of | ||||
| * the time this check works. | * the time this check works. | ||||
| */ | */ | ||||
| error = EEXIST; | error = EEXIST; | ||||
| goto out; | goto out; | ||||
| } | } | ||||
| if (tvp == NULL && (flags & AT_RENAME_EXCHANGE) != 0) { | |||||
| error = ENOENT; | |||||
| goto out; | |||||
| } | |||||
| error = vn_start_write(fvp, &mp, V_NOWAIT); | error = vn_start_write(fvp, &mp, V_NOWAIT); | ||||
| if (error != 0) { | if (error != 0) { | ||||
| again1: | again1: | ||||
| NDFREE_PNBUF(&fromnd); | NDFREE_PNBUF(&fromnd); | ||||
| NDFREE_PNBUF(&tond); | NDFREE_PNBUF(&tond); | ||||
| if (tvp != NULL) | if (tvp != NULL) | ||||
| vput(tvp); | vput(tvp); | ||||
| if (tdvp == tvp) | if (tdvp == tvp) | ||||
| Show All 26 Lines | again1: | ||||
| } | } | ||||
| irflag = vn_irflag_read(fvp); | irflag = vn_irflag_read(fvp); | ||||
| if (((irflag & VIRF_NAMEDATTR) != 0 && tdvp != fromnd.ni_dvp) || | if (((irflag & VIRF_NAMEDATTR) != 0 && tdvp != fromnd.ni_dvp) || | ||||
| (irflag & VIRF_NAMEDDIR) != 0) { | (irflag & VIRF_NAMEDDIR) != 0) { | ||||
| error = EINVAL; | error = EINVAL; | ||||
| goto out; | goto out; | ||||
| } | } | ||||
| if (tvp != NULL) { | if (tvp != NULL) { | ||||
| if ((flags & AT_RENAME_EXCHANGE) == 0) { | |||||
| if (fvp->v_type == VDIR && tvp->v_type != VDIR) { | if (fvp->v_type == VDIR && tvp->v_type != VDIR) { | ||||
| error = ENOTDIR; | error = ENOTDIR; | ||||
| goto out; | goto out; | ||||
| } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { | } else if (fvp->v_type != VDIR && | ||||
| tvp->v_type == VDIR) { | |||||
| error = EISDIR; | error = EISDIR; | ||||
| goto out; | goto out; | ||||
| } | |||||
| } | } | ||||
| #ifdef CAPABILITIES | #ifdef CAPABILITIES | ||||
| if (newfd != AT_FDCWD && (tond.ni_resflags & NIRES_ABS) == 0) { | if (newfd != AT_FDCWD && (tond.ni_resflags & NIRES_ABS) == 0) { | ||||
| /* | /* | ||||
| * If the target already exists we require CAP_UNLINKAT | * If the target already exists we require CAP_UNLINKAT | ||||
| * from 'newfd', when newfd was used for the lookup. | * from 'newfd', when newfd was used for the lookup. | ||||
| */ | */ | ||||
| error = cap_check(&tond.ni_filecaps.fc_rights, | error = cap_check(&tond.ni_filecaps.fc_rights, | ||||
| ▲ Show 20 Lines • Show All 1,376 Lines • Show Last 20 Lines | |||||
The Linux man pages I looked at seem to indicate EINVAL should be returned if both NOREPLACE and EXCHANGE are set, since they're mutually exclusive. Do we need to be concerned about that level of compatibility? The below check that returns EEXIST will still avoid modifying the target file in that case.