X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/04b8595b18b1b41ac7a206e4b3d51a635f8413d7..4bd07ac2140668789aa3ee8ec4dde4a3e0a3bba5:/bsd/vfs/vfs_syscalls.c?ds=inline diff --git a/bsd/vfs/vfs_syscalls.c b/bsd/vfs/vfs_syscalls.c index ba37a4e38..adea23a19 100644 --- a/bsd/vfs/vfs_syscalls.c +++ b/bsd/vfs/vfs_syscalls.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995-2014 Apple Inc. All rights reserved. + * Copyright (c) 1995-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -99,6 +99,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,7 @@ #include #include +#include #if CONFIG_MACF #include @@ -166,6 +168,18 @@ int prepare_coveredvp(vnode_t vp, vfs_context_t ctx, struct componentname *cnp, struct fd_vn_data * fg_vn_data_alloc(void); +/* + * Max retries for ENOENT returns from vn_authorize_{rmdir, unlink, rename} + * Concurrent lookups (or lookups by ids) on hard links can cause the + * vn_getpath (which does not re-enter the filesystem as vn_getpath_fsenter + * does) to return ENOENT as the path cannot be returned from the name cache + * alone. We have no option but to retry and hope to get one namei->reverse path + * generation done without an intervening lookup, lookup by id on the hard link + * item. This is only an issue for MAC hooks which cannot reenter the filesystem + * which currently are the MAC hooks for rename, unlink and rmdir. + */ +#define MAX_AUTHORIZE_ENOENT_RETRIES 1024 + static int rmdirat_internal(vfs_context_t, int, user_addr_t, enum uio_seg); static int fsgetpath_internal(vfs_context_t, int, uint64_t, vm_size_t, caddr_t, int *); @@ -185,7 +199,7 @@ __private_extern__ int sync_internal(void); __private_extern__ -int unlink1(vfs_context_t, struct nameidata *, int); +int unlink1(vfs_context_t, vnode_t, user_addr_t, enum uio_seg, int); extern lck_grp_t *fd_vn_lck_grp; extern lck_grp_attr_t *fd_vn_lck_grp_attr; @@ -400,6 +414,14 @@ __mac_mount(struct proc *p, register struct __mac_mount_args *uap, __unused int3 AUDIT_ARG(fflags, flags); +#if SECURE_KERNEL + if (flags & MNT_UNION) { + /* No union mounts on release kernels */ + error = EPERM; + goto out; + } +#endif + if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_ROOTFS)) { if (!(flags & MNT_UNION)) { @@ -417,7 +439,7 @@ __mac_mount(struct proc *p, register struct __mac_mount_args *uap, __unused int3 flags = (flags & ~(MNT_UPDATE)); } -#ifdef SECURE_KERNEL +#if SECURE_KERNEL if ((flags & MNT_RDONLY) == 0) { /* Release kernels are not allowed to mount "/" as rw */ error = EPERM; @@ -726,7 +748,7 @@ update: if ( (error = namei(&nd)) ) goto out1; - strncpy(mp->mnt_vfsstat.f_mntfromname, nd.ni_cnd.cn_pnbuf, MAXPATHLEN); + strlcpy(mp->mnt_vfsstat.f_mntfromname, nd.ni_cnd.cn_pnbuf, MAXPATHLEN); devvp = nd.ni_vp; nameidone(&nd); @@ -977,6 +999,8 @@ update: /* Now that mount is setup, notify the listeners */ vfs_notify_mount(pvp); + IOBSDMountChange(mp, kIOMountChangeMount); + } else { /* If we fail a fresh mount, there should be no vnodes left hooked into the mountpoint. */ if (mp->mnt_vnodelist.tqh_first != NULL) { @@ -1512,8 +1536,8 @@ relocate_imageboot_source(vnode_t pvp, vnode_t vp, struct componentname *cnp, placed = TRUE; - strncpy(old_mntonname, mp->mnt_vfsstat.f_mntonname, MAXPATHLEN); - strncpy(mp->mnt_vfsstat.f_mntonname, cnp->cn_pnbuf, MAXPATHLEN); + strlcpy(old_mntonname, mp->mnt_vfsstat.f_mntonname, MAXPATHLEN); + strlcpy(mp->mnt_vfsstat.f_mntonname, cnp->cn_pnbuf, MAXPATHLEN); /* Forbid future moves */ mount_lock(mp); @@ -1538,7 +1562,7 @@ relocate_imageboot_source(vnode_t pvp, vnode_t vp, struct componentname *cnp, return 0; out3: - strncpy(mp->mnt_vfsstat.f_mntonname, old_mntonname, MAXPATHLEN); + strlcpy(mp->mnt_vfsstat.f_mntonname, old_mntonname, MAXPATHLEN); mount_lock(mp); mp->mnt_kern_flag &= ~(MNTK_HAS_MOVED); @@ -1908,6 +1932,8 @@ dounmount(struct mount *mp, int flags, int withref, vfs_context_t ctx) } } + IOBSDMountChange(mp, kIOMountChangeUnmount); + #if CONFIG_TRIGGERS vfs_nested_trigger_unmounts(mp, flags, ctx); did_vflush = 1; @@ -3283,15 +3309,16 @@ open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, int flags, oflags; int type, indx, error; struct flock lf; - int no_controlling_tty = 0; - int deny_controlling_tty = 0; - struct session *sessp = SESSION_NULL; + struct vfs_context context; oflags = uflags; if ((oflags & O_ACCMODE) == O_ACCMODE) return(EINVAL); + flags = FFLAGS(uflags); + CLR(flags, FENCRYPTED); + CLR(flags, FUNENCRYPTED); AUDIT_ARG(fflags, oflags); AUDIT_ARG(mode, vap->va_mode); @@ -3302,68 +3329,26 @@ open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, } uu->uu_dupfd = -indx - 1; - if (!(p->p_flag & P_CONTROLT)) { - sessp = proc_session(p); - no_controlling_tty = 1; - /* - * If conditions would warrant getting a controlling tty if - * the device being opened is a tty (see ttyopen in tty.c), - * but the open flags deny it, set a flag in the session to - * prevent it. - */ - if (SESS_LEADER(p, sessp) && - sessp->s_ttyvp == NULL && - (flags & O_NOCTTY)) { - session_lock(sessp); - sessp->s_flags |= S_NOCTTY; - session_unlock(sessp); - deny_controlling_tty = 1; - } - } - if ((error = vn_open_auth(ndp, &flags, vap))) { if ((error == ENODEV || error == ENXIO) && (uu->uu_dupfd >= 0)){ /* XXX from fdopen */ if ((error = dupfdopen(p->p_fd, indx, uu->uu_dupfd, flags, error)) == 0) { fp_drop(p, indx, NULL, 0); *retval = indx; - if (deny_controlling_tty) { - session_lock(sessp); - sessp->s_flags &= ~S_NOCTTY; - session_unlock(sessp); - } - if (sessp != SESSION_NULL) - session_rele(sessp); return (0); } } if (error == ERESTART) error = EINTR; fp_free(p, indx, fp); - - if (deny_controlling_tty) { - session_lock(sessp); - sessp->s_flags &= ~S_NOCTTY; - session_unlock(sessp); - } - if (sessp != SESSION_NULL) - session_rele(sessp); return (error); } uu->uu_dupfd = 0; vp = ndp->ni_vp; - fp->f_fglob->fg_flag = flags & (FMASK | O_EVTONLY); + fp->f_fglob->fg_flag = flags & (FMASK | O_EVTONLY | FENCRYPTED | FUNENCRYPTED); fp->f_fglob->fg_ops = &vnops; fp->f_fglob->fg_data = (caddr_t)vp; -#if CONFIG_PROTECT - if (VATTR_IS_ACTIVE (vap, va_dataprotect_flags)) { - if (vap->va_dataprotect_flags & VA_DP_RAWENCRYPTED) { - fp->f_fglob->fg_flag |= FENCRYPTED; - } - } -#endif - if (flags & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; @@ -3390,33 +3375,6 @@ open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, if ((flags & O_TRUNC) && ((error = vnode_setsize(vp, (off_t)0, 0, ctx)) != 0)) goto bad; - /* - * If the open flags denied the acquisition of a controlling tty, - * clear the flag in the session structure that prevented the lower - * level code from assigning one. - */ - if (deny_controlling_tty) { - session_lock(sessp); - sessp->s_flags &= ~S_NOCTTY; - session_unlock(sessp); - } - - /* - * If a controlling tty was set by the tty line discipline, then we - * want to set the vp of the tty into the session structure. We have - * a race here because we can't get to the vp for the tp in ttyopen, - * because it's not passed as a parameter in the open path. - */ - if (no_controlling_tty && (p->p_flag & P_CONTROLT)) { - vnode_t ttyvp; - - session_lock(sessp); - ttyvp = sessp->s_ttyvp; - sessp->s_ttyvp = vp; - sessp->s_ttyvid = vnode_vid(vp); - session_unlock(sessp); - } - /* * For directories we hold some additional information in the fd. */ @@ -3428,6 +3386,18 @@ open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, vnode_put(vp); + /* + * The first terminal open (without a O_NOCTTY) by a session leader + * results in it being set as the controlling terminal. + */ + if (vnode_istty(vp) && !(p->p_flag & P_CONTROLT) && + !(flags & O_NOCTTY)) { + int tmp = 0; + + (void)(*fp->f_fglob->fg_ops->fo_ioctl)(fp, (int)TIOCSCTTY, + (caddr_t)&tmp, ctx); + } + proc_fdlock(p); if (flags & O_CLOEXEC) *fdflags(p, indx) |= UF_EXCLOSE; @@ -3439,19 +3409,9 @@ open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, *retval = indx; - if (sessp != SESSION_NULL) - session_rele(sessp); return (0); bad: - if (deny_controlling_tty) { - session_lock(sessp); - sessp->s_flags &= ~S_NOCTTY; - session_unlock(sessp); - } - if (sessp != SESSION_NULL) - session_rele(sessp); - - struct vfs_context context = *vfs_context_current(); + context = *vfs_context_current(); context.vc_ucred = fp->f_fglob->fg_cred; if ((fp->f_fglob->fg_flag & FHASLOCK) && @@ -3617,15 +3577,27 @@ int open_dprotected_np (__unused proc_t p, struct open_dprotected_np_args *uap, * 2. set a flag to mark it as requiring open-raw-encrypted semantics. */ if (flags & O_CREAT) { - VATTR_SET(&va, va_dataprotect_class, class); + /* lower level kernel code validates that the class is valid before applying it. */ + if (class != PROTECTION_CLASS_DEFAULT) { + /* + * PROTECTION_CLASS_DEFAULT implies that we make the class for this + * file behave the same as open (2) + */ + VATTR_SET(&va, va_dataprotect_class, class); + } } - if (dpflags & O_DP_GETRAWENCRYPTED) { + if (dpflags & (O_DP_GETRAWENCRYPTED|O_DP_GETRAWUNENCRYPTED)) { if ( flags & (O_RDWR | O_WRONLY)) { /* Not allowed to write raw encrypted bytes */ return EINVAL; } - VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED); + if (uap->dpflags & O_DP_GETRAWENCRYPTED) { + VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWENCRYPTED); + } + if (uap->dpflags & O_DP_GETRAWUNENCRYPTED) { + VATTR_SET(&va, va_dataprotect_flags, VA_DP_RAWUNENCRYPTED); + } } error = open1(vfs_context_current(), &nd, uap->flags, &va, @@ -3804,9 +3776,6 @@ mknod(proc_t p, struct mknod_args *uap, __unused int32_t *retval) } switch (uap->mode & S_IFMT) { - case S_IFMT: /* used by badsect to flag bad sectors */ - VATTR_SET(&va, va_type, VBAD); - break; case S_IFCHR: VATTR_SET(&va, va_type, VCHR); break; @@ -4341,17 +4310,18 @@ symlinkat_internal(vfs_context_t ctx, user_addr_t path_data, int fd, error = VNOP_SYMLINK(dvp, &vp, &nd.ni_cnd, &va, path, ctx); #if CONFIG_MACF - if (error == 0) + if (error == 0 && vp) error = vnode_label(vnode_mount(vp), dvp, vp, &nd.ni_cnd, VNODE_LABEL_CREATE, ctx); #endif /* do fallback attribute handling */ - if (error == 0) + if (error == 0 && vp) error = vnode_setattr_fallback(vp, &va, ctx); if (error == 0) { int update_flags = 0; + /*check if a new vnode was created, else try to get one*/ if (vp == NULL) { nd.ni_cnd.cn_nameiop = LOOKUP; #if CONFIG_TRIGGERS @@ -4452,8 +4422,10 @@ undelete(__unused proc_t p, __unused struct undelete_args *uap, __unused int32_t */ /* ARGSUSED */ static int -unlink1at(vfs_context_t ctx, struct nameidata *ndp, int unlink_flags, int fd) +unlinkat_internal(vfs_context_t ctx, int fd, vnode_t start_dvp, + user_addr_t path_arg, enum uio_seg segflg, int unlink_flags) { + struct nameidata nd; vnode_t vp, dvp; int error; struct componentname *cnp; @@ -4463,29 +4435,49 @@ unlink1at(vfs_context_t ctx, struct nameidata *ndp, int unlink_flags, int fd) fse_info finfo; struct vnode_attr va; #endif - int flags = 0; - int need_event = 0; - int has_listeners = 0; - int truncated_path=0; + int flags; + int need_event; + int has_listeners; + int truncated_path; int batched; - struct vnode_attr *vap = NULL; + struct vnode_attr *vap; + int do_retry; + int retry_count = 0; + int cn_flags; + + cn_flags = LOCKPARENT; + if (!(unlink_flags & VNODE_REMOVE_NO_AUDIT_PATH)) + cn_flags |= AUDITVNPATH1; + /* If a starting dvp is passed, it trumps any fd passed. */ + if (start_dvp) + cn_flags |= USEDVP; #if NAMEDRSRCFORK /* unlink or delete is allowed on rsrc forks and named streams */ - ndp->ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; + cn_flags |= CN_ALLOWRSRCFORK; #endif - ndp->ni_cnd.cn_flags |= LOCKPARENT; - ndp->ni_flag |= NAMEI_COMPOUNDREMOVE; - cnp = &ndp->ni_cnd; +retry: + do_retry = 0; + flags = 0; + need_event = 0; + has_listeners = 0; + truncated_path = 0; + vap = NULL; + + NDINIT(&nd, DELETE, OP_UNLINK, cn_flags, segflg, path_arg, ctx); + + nd.ni_dvp = start_dvp; + nd.ni_flag |= NAMEI_COMPOUNDREMOVE; + cnp = &nd.ni_cnd; lookup_continue: - error = nameiat(ndp, fd); + error = nameiat(&nd, fd); if (error) return (error); - dvp = ndp->ni_dvp; - vp = ndp->ni_vp; + dvp = nd.ni_dvp; + vp = nd.ni_vp; /* With Carbon delete semantics, busy files cannot be deleted */ @@ -4510,6 +4502,13 @@ lookup_continue: if (!batched) { error = vn_authorize_unlink(dvp, vp, cnp, ctx, NULL); if (error) { + if (error == ENOENT) { + assert(retry_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (retry_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + do_retry = 1; + retry_count++; + } + } goto out; } } @@ -4548,23 +4547,23 @@ lookup_continue: goto out; } } - len = safe_getpath(dvp, ndp->ni_cnd.cn_nameptr, path, MAXPATHLEN, &truncated_path); + len = safe_getpath(dvp, nd.ni_cnd.cn_nameptr, path, MAXPATHLEN, &truncated_path); } #if NAMEDRSRCFORK - if (ndp->ni_cnd.cn_flags & CN_WANTSRSRCFORK) + if (nd.ni_cnd.cn_flags & CN_WANTSRSRCFORK) error = vnode_removenamedstream(dvp, vp, XATTR_RESOURCEFORK_NAME, 0, ctx); else #endif { - error = vn_remove(dvp, &ndp->ni_vp, ndp, flags, vap, ctx); - vp = ndp->ni_vp; + error = vn_remove(dvp, &nd.ni_vp, &nd, flags, vap, ctx); + vp = nd.ni_vp; if (error == EKEEPLOOKING) { if (!batched) { panic("EKEEPLOOKING, but not a filesystem that supports compound VNOPs?"); } - if ((ndp->ni_flag & NAMEI_CONTLOOKUP) == 0) { + if ((nd.ni_flag & NAMEI_CONTLOOKUP) == 0) { panic("EKEEPLOOKING, but continue flag not set?"); } @@ -4573,6 +4572,18 @@ lookup_continue: goto out; } goto lookup_continue; + } else if (error == ENOENT && batched) { + assert(retry_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (retry_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + /* + * For compound VNOPs, the authorization callback may + * return ENOENT in case of racing hardlink lookups + * hitting the name cache, redrive the lookup. + */ + do_retry = 1; + retry_count += 1; + goto out; + } } } @@ -4635,39 +4646,45 @@ out: * nameidone has to happen before we vnode_put(dvp) * since it may need to release the fs_nodelock on the dvp */ - nameidone(ndp); + nameidone(&nd); vnode_put(dvp); if (vp) { vnode_put(vp); } + + if (do_retry) { + goto retry; + } + return (error); } int -unlink1(vfs_context_t ctx, struct nameidata *ndp, int unlink_flags) +unlink1(vfs_context_t ctx, vnode_t start_dvp, user_addr_t path_arg, + enum uio_seg segflg, int unlink_flags) { - return (unlink1at(ctx, ndp, unlink_flags, AT_FDCWD)); + return (unlinkat_internal(ctx, AT_FDCWD, start_dvp, path_arg, segflg, + unlink_flags)); } /* - * Delete a name from the filesystem using POSIX semantics. + * Delete a name from the filesystem using Carbon semantics. */ -static int -unlinkat_internal(vfs_context_t ctx, int fd, user_addr_t path, - enum uio_seg segflg) +int +delete(__unused proc_t p, struct delete_args *uap, __unused int32_t *retval) { - struct nameidata nd; - - NDINIT(&nd, DELETE, OP_UNLINK, AUDITVNPATH1, segflg, - path, ctx); - return (unlink1at(ctx, &nd, 0, fd)); + return (unlinkat_internal(vfs_context_current(), AT_FDCWD, NULLVP, + uap->path, UIO_USERSPACE, VNODE_REMOVE_NODELETEBUSY)); } +/* + * Delete a name from the filesystem using POSIX semantics. + */ int unlink(__unused proc_t p, struct unlink_args *uap, __unused int32_t *retval) { - return (unlinkat_internal(vfs_context_current(), AT_FDCWD, uap->path, - UIO_USERSPACE)); + return (unlinkat_internal(vfs_context_current(), AT_FDCWD, NULLVP, + uap->path, UIO_USERSPACE, 0)); } int @@ -4681,21 +4698,7 @@ unlinkat(__unused proc_t p, struct unlinkat_args *uap, __unused int32_t *retval) uap->path, UIO_USERSPACE)); else return (unlinkat_internal(vfs_context_current(), uap->fd, - uap->path, UIO_USERSPACE)); -} - -/* - * Delete a name from the filesystem using Carbon semantics. - */ -int -delete(__unused proc_t p, struct delete_args *uap, __unused int32_t *retval) -{ - struct nameidata nd; - vfs_context_t ctx = vfs_context_current(); - - NDINIT(&nd, DELETE, OP_UNLINK, AUDITVNPATH1, UIO_USERSPACE, - uap->path, ctx); - return unlink1(ctx, &nd, VNODE_REMOVE_NODELETEBUSY); + NULLVP, uap->path, UIO_USERSPACE, 0)); } /* @@ -6505,7 +6508,7 @@ copyfile(__unused proc_t p, struct copyfile_args *uap, __unused int32_t *retval) return(EINVAL); } - NDINIT(&fromnd, LOOKUP, OP_COPYFILE, SAVESTART | AUDITVNPATH1, + NDINIT(&fromnd, LOOKUP, OP_COPYFILE, AUDITVNPATH1, UIO_USERSPACE, uap->from, ctx); if ((error = namei(&fromnd))) return (error); @@ -6560,8 +6563,6 @@ out: out1: vnode_put(fvp); - if (fromnd.ni_startdir) - vnode_put(fromnd.ni_startdir); nameidone(&fromnd); if (error == -1) @@ -6584,6 +6585,7 @@ renameat_internal(vfs_context_t ctx, int fromfd, user_addr_t from, struct nameidata *fromnd, *tond; int error; int do_retry; + int retry_count; int mntrename; int need_event; const char *oname = NULL; @@ -6610,6 +6612,7 @@ renameat_internal(vfs_context_t ctx, int fromfd, user_addr_t from, holding_mntlock = 0; do_retry = 0; + retry_count = 0; retry: fvp = tvp = NULL; fdvp = tdvp = NULL; @@ -6671,12 +6674,16 @@ continue_lookup: error = vn_authorize_rename(fdvp, fvp, &fromnd->ni_cnd, tdvp, tvp, &tond->ni_cnd, ctx, NULL); if (error) { if (error == ENOENT) { - /* - * We encountered a race where after doing the namei, tvp stops - * being valid. If so, simply re-drive the rename call from the - * top. - */ - do_retry = 1; + assert(retry_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (retry_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + /* + * We encountered a race where after doing the namei, tvp stops + * being valid. If so, simply re-drive the rename call from the + * top. + */ + do_retry = 1; + retry_count += 1; + } } goto out1; } @@ -6944,6 +6951,19 @@ skipped_lookup: do_retry = 1; } + /* + * For compound VNOPs, the authorization callback may return + * ENOENT in case of racing hardlink lookups hitting the name + * cache, redrive the lookup. + */ + if (batched && error == ENOENT) { + assert(retry_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (retry_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + do_retry = 1; + retry_count += 1; + } + } + goto out1; } @@ -7318,6 +7338,7 @@ rmdirat_internal(vfs_context_t ctx, int fd, user_addr_t dirpath, struct vnode_attr va; #endif /* CONFIG_FSE */ struct vnode_attr *vap = NULL; + int restart_count = 0; int batched; int restart_flag; @@ -7367,6 +7388,13 @@ continue_lookup: if (!batched) { error = vn_authorize_rmdir(dvp, vp, &nd.ni_cnd, ctx, NULL); if (error) { + if (error == ENOENT) { + assert(restart_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (restart_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + restart_flag = 1; + restart_count += 1; + } + } goto out; } } @@ -7422,6 +7450,18 @@ continue_lookup: if (error == EKEEPLOOKING) { goto continue_lookup; + } else if (batched && error == ENOENT) { + assert(restart_count < MAX_AUTHORIZE_ENOENT_RETRIES); + if (restart_count < MAX_AUTHORIZE_ENOENT_RETRIES) { + /* + * For compound VNOPs, the authorization callback + * may return ENOENT in case of racing hard link lookups + * redrive the lookup. + */ + restart_flag = 1; + restart_count += 1; + goto out; + } } #if CONFIG_APPLEDOUBLE /* @@ -9236,11 +9276,12 @@ fsctl_internal(proc_t p, vnode_t *arg_vp, u_long cmd, user_addr_t udata, u_long memp = NULL; + /* * ensure the buffer is large enough for underlying calls */ #ifndef HFSIOC_GETPATH -typedef char pn_t[MAXPATHLEN]; + typedef char pn_t[MAXPATHLEN]; #define HFSIOC_GETPATH _IOWR('h', 13, pn_t) #endif @@ -9252,7 +9293,6 @@ typedef char pn_t[MAXPATHLEN]; size = MAXPATHLEN; } - if (size > sizeof (stkbuf)) { if ((memp = (caddr_t)kalloc(size)) == 0) return ENOMEM; data = memp; @@ -9647,27 +9687,30 @@ ffsctl (proc_t p, struct ffsctl_args *uap, __unused int32_t *retval) /* Get the vnode for the file we are getting info on: */ if ((error = file_vnode(uap->fd, &vp))) - goto done; + return error; fd = uap->fd; if ((error = vnode_getwithref(vp))) { - goto done; + file_drop(fd); + return error; } #if CONFIG_MACF - error = mac_mount_check_fsctl(ctx, vnode_mount(vp), uap->cmd); - if (error) { - goto done; + if ((error = mac_mount_check_fsctl(ctx, vnode_mount(vp), uap->cmd))) { + file_drop(fd); + vnode_put(vp); + return error; } #endif error = fsctl_internal(p, &vp, uap->cmd, (user_addr_t)uap->data, uap->options, ctx); -done: - if (fd != -1) - file_drop(fd); + file_drop(fd); - if (vp) + /*validate vp; fsctl_internal() can drop iocount and reset vp to NULL*/ + if (vp) { vnode_put(vp); + } + return error; } /* end of fsctl system call */ @@ -9887,7 +9930,12 @@ fsetxattr(proc_t p, struct fsetxattr_args *uap, int *retval) return (EINVAL); if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) { - return (error); + if (error == EPERM) { + /* if the string won't fit in attrname, copyinstr emits EPERM */ + return (ENAMETOOLONG); + } + /* Otherwise return the default error from copyinstr to detect ERANGE, etc */ + return error; } if (xattr_protected(attrname)) return(EPERM);