/* * given an absolute pathname, convert it, if possible, to a devfs * name. Examples: * /dev/rsd3a to /pci@1f,4000/glm@3/sd@0,0:a * /dev/dsk/c0t0d0s0 to /pci@1f,4000/glm@3/sd@0,0:a * /devices/pci@1f,4000/glm@3/sd@0,0:a to /pci@1f,4000/glm@3/sd@0,0:a * /pci@1f,4000/glm@3/sd@0,0:a unchanged * * This routine deals with symbolic links, physical pathname with and * without /devices stripped. Returns 0 on success or -1 on failure. */ static int resolve_devfs_name(char *name, char *buffer) { int error; char *fullname = NULL; struct pathname pn, rpn; /* if not a /dev or /device name, prepend /devices */ if (strncmp(name, "/dev/", 5) != 0 && strncmp(name, "/devices/", 9) != 0) { fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP); (void) snprintf(fullname, MAXPATHLEN, "/devices%s", name); name = fullname; } if (pn_get(name, UIO_SYSSPACE, &pn) != 0) { if (fullname) kmem_free(fullname, MAXPATHLEN); return (-1); } pn_alloc(&rpn); error = lookuppn(&pn, &rpn, FOLLOW, NULL, NULL); if (error == 0) bcopy(rpn.pn_path, buffer, rpn.pn_pathlen); pn_free(&pn); pn_free(&rpn); if (fullname) kmem_free(fullname, MAXPATHLEN); return (error); }
static int is_nonempty_dir(char *name, char *pathleft, struct sdev_node *dir) { struct match_arg marg; struct pathname pn; struct vnode *gvp; struct sdev_node *gdir = dir->sdev_origin; if (VOP_LOOKUP(SDEVTOV(gdir), name, &gvp, NULL, 0, NULL, kcred, NULL, NULL, NULL) != 0) return (0); if (gvp->v_type != VDIR) { VN_RELE(gvp); return (0); } if (pn_get(pathleft, UIO_SYSSPACE, &pn) != 0) { VN_RELE(gvp); return (0); } marg.expr = kmem_alloc(MAXNAMELEN, KM_SLEEP); (void) pn_getcomponent(&pn, marg.expr); marg.match = 0; walk_dir(gvp, &marg, match_name); VN_RELE(gvp); kmem_free(marg.expr, MAXNAMELEN); pn_free(&pn); return (marg.match); }
/* * Lookup the user file name, * Handle allocation and freeing of pathname buffer, return error. */ int lookupnameatcred( char *fnamep, /* user pathname */ enum uio_seg seg, /* addr space that name is in */ int followlink, /* follow sym links */ vnode_t **dirvpp, /* ret for ptr to parent dir vnode */ vnode_t **compvpp, /* ret for ptr to component vnode */ vnode_t *startvp, /* start path search from vp */ cred_t *cr) /* credential */ { char namebuf[TYPICALMAXPATHLEN]; struct pathname lookpn; int error; error = pn_get_buf(fnamep, seg, &lookpn, namebuf, sizeof (namebuf)); if (error == 0) { error = lookuppnatcred(&lookpn, NULL, followlink, dirvpp, compvpp, startvp, cr); } if (error == ENAMETOOLONG) { /* * This thread used a pathname > TYPICALMAXPATHLEN bytes long. */ if (error = pn_get(fnamep, seg, &lookpn)) return (error); error = lookuppnatcred(&lookpn, NULL, followlink, dirvpp, compvpp, startvp, cr); pn_free(&lookpn); } return (error); }
/* * Callback supporting lookup in a GFS XATTR directory. */ static int xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop, cred_t *cr, int flags, int *deflags, pathname_t *rpnp) { vnode_t *pvp; struct pathname pn; int error; *vpp = NULL; *inop = 0; error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, NULL); /* * Return ENOENT for EACCES requests during lookup. Once an * attribute create is attempted EACCES will be returned. */ if (error) { if (error == EACCES) return (ENOENT); return (error); } error = pn_get((char *)nm, UIO_SYSSPACE, &pn); if (error == 0) { error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp, cr, NULL, deflags, rpnp); pn_free(&pn); } return (error); }
/* * readdir_xattr_casecmp: given a system attribute name, see if there * is a real xattr with the same normalized name. */ static int readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, int *eflags) { int error; vnode_t *vp; struct pathname pn; *eflags = 0; error = pn_get(nm, UIO_SYSSPACE, &pn); if (error == 0) { error = VOP_LOOKUP(dvp, nm, &vp, &pn, FIGNORECASE, rootvp, cr, ct, NULL, NULL); if (error == 0) { *eflags = ED_CASE_CONFLICT; VN_RELE(vp); } else if (error == ENOENT) { error = 0; } pn_free(&pn); } return (error); }
/* * Given a global path (from rootdir), and a vnode that is the current root, * return the portion of the path that is beneath the current root or NULL on * failure. The path MUST be a resolved path (no '..' entries or symlinks), * otherwise this function will fail. */ static char * localpath(char *path, struct vnode *vrootp, cred_t *cr) { vnode_t *vp; vnode_t *cvp; char component[MAXNAMELEN]; char *ret = NULL; pathname_t pn; /* * We use vn_compare() instead of VN_CMP() in order to detect lofs * mounts and stacked vnodes. */ if (vn_compare(vrootp, rootdir)) return (path); if (pn_get(path, UIO_SYSSPACE, &pn) != 0) return (NULL); vp = rootdir; VN_HOLD(vp); if (vn_ismntpt(vp) && traverse(&vp) != 0) { VN_RELE(vp); pn_free(&pn); return (NULL); } while (pn_pathleft(&pn)) { pn_skipslash(&pn); if (pn_getcomponent(&pn, component) != 0) break; if (VOP_LOOKUP(vp, component, &cvp, &pn, 0, rootdir, cr, NULL, NULL, NULL) != 0) break; VN_RELE(vp); vp = cvp; if (vn_ismntpt(vp) && traverse(&vp) != 0) break; if (vn_compare(vp, vrootp)) { ret = path + (pn.pn_path - pn.pn_buf); break; } } VN_RELE(vp); pn_free(&pn); return (ret); }
/* * Look up a logical name in the global zone. * Provides the ability to map the global zone's device name * to an alternate name within a zone. The primary example * is the virtual console device /dev/zcons/[zonename]/zconsole * mapped to /[zonename]/root/dev/zconsole. */ static void prof_lookup_globaldev(struct sdev_node *dir, struct sdev_node *gdir, char *name, char *rename) { int error; struct vnode *avp, *gdv, *gddv; struct sdev_node *newdv; struct vattr vattr = {0}; struct pathname pn; /* check if node already exists */ newdv = sdev_cache_lookup(dir, rename); if (newdv) { ASSERT(newdv->sdev_state != SDEV_ZOMBIE); SDEV_SIMPLE_RELE(newdv); return; } /* sanity check arguments */ if (!gdir || pn_get(name, UIO_SYSSPACE, &pn)) return; /* perform a relative lookup of the global /dev instance */ gddv = SDEVTOV(gdir); VN_HOLD(gddv); error = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &gdv, rootdir, gddv, kcred); pn_free(&pn); if (error) { sdcmn_err10(("prof_lookup_globaldev: %s not found\n", name)); return; } ASSERT(gdv && gdv->v_type != VLNK); /* * Found the entry in global /dev, figure out attributes * by looking at backing store. Call into devfs for default. * Note, mapped device is persisted under the new name */ prof_getattr(dir, rename, gdv, &vattr, &avp, NULL); if (gdv->v_type != VDIR) { VN_RELE(gdv); gdir = NULL; } else gdir = VTOSDEV(gdv); if (prof_mknode(dir, rename, &newdv, &vattr, avp, (void *)gdir, kcred) == 0) { ASSERT(newdv->sdev_state != SDEV_ZOMBIE); SDEV_SIMPLE_RELE(newdv); } }
/* * Last chance for a zone to see a node. If our parent dir is * SDEV_ZONED, then we look up the "zone" property for the node. If the * property is found and matches the current zone name, we allow it. * Note that this isn't quite correct for the global zone peeking inside * a zone's /dev - for that to work, we'd have to have a per-dev-mount * zone ref squirreled away. */ static int prof_zone_matched(char *name, struct sdev_node *dir) { vnode_t *gvn = SDEVTOV(dir->sdev_origin); struct pathname pn; vnode_t *vn = NULL; char zonename[ZONENAME_MAX]; int znlen = ZONENAME_MAX; int ret; ASSERT((dir->sdev_flags & SDEV_ZONED) != 0); sdcmn_err10(("sdev_node %p is zoned, looking for %s\n", (void *)dir, name)); if (pn_get(name, UIO_SYSSPACE, &pn)) return (0); VN_HOLD(gvn); ret = lookuppnvp(&pn, NULL, FOLLOW, NULLVPP, &vn, rootdir, gvn, kcred); pn_free(&pn); if (ret != 0) { sdcmn_err10(("prof_zone_matched: %s not found\n", name)); return (0); } /* * VBLK doesn't matter, and the property name is in fact treated * as a const char *. */ ret = e_ddi_getlongprop_buf(vn->v_rdev, VBLK, (char *)"zone", DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (caddr_t)zonename, &znlen); VN_RELE(vn); if (ret == DDI_PROP_NOT_FOUND) { sdcmn_err10(("vnode %p: no zone prop\n", (void *)vn)); return (0); } else if (ret != DDI_PROP_SUCCESS) { sdcmn_err10(("vnode %p: zone prop error: %d\n", (void *)vn, ret)); return (0); } sdcmn_err10(("vnode %p zone prop: %s\n", (void *)vn, zonename)); return (strcmp(zonename, curproc->p_zone->zone_name) == 0); }
/* * Parse path components and apply requested matching rule at * directory level. */ static void process_rule(struct sdev_node *dir, struct sdev_node *gdir, char *path, char *tgt, int type) { char *name; struct pathname pn; int rv = 0; if ((strlen(path) > 5) && (strncmp(path, "/dev/", 5) == 0)) { path += 5; } if (pn_get(path, UIO_SYSSPACE, &pn) != 0) return; name = kmem_alloc(MAXPATHLEN, KM_SLEEP); (void) pn_getcomponent(&pn, name); pn_skipslash(&pn); SDEV_HOLD(dir); while (pn_pathleft(&pn)) { /* If this is pattern, just add the pattern */ if (strpbrk(name, "*?[]") != NULL && (type == PROFILE_TYPE_INCLUDE || type == PROFILE_TYPE_EXCLUDE)) { ASSERT(tgt == NULL); tgt = pn.pn_path; break; } if ((rv = prof_make_dir(name, &gdir, &dir)) != 0) { cmn_err(CE_CONT, "process_rule: %s error %d\n", path, rv); break; } (void) pn_getcomponent(&pn, name); pn_skipslash(&pn); } /* process the leaf component */ if (rv == 0) { prof_add_rule(name, tgt, dir, type); SDEV_SIMPLE_RELE(dir); } kmem_free(name, MAXPATHLEN); pn_free(&pn); }
static int copen(int startfd, char *fname, int filemode, int createmode) { struct pathname pn; vnode_t *vp, *sdvp; file_t *fp, *startfp; enum vtype type; int error; int fd, dupfd; vnode_t *startvp; proc_t *p = curproc; uio_seg_t seg = UIO_USERSPACE; char *open_filename = fname; uint32_t auditing = AU_AUDITING(); char startchar; if (filemode & (FSEARCH|FEXEC)) { /* * Must be one or the other and neither FREAD nor FWRITE * Must not be any of FAPPEND FCREAT FTRUNC FXATTR FXATTRDIROPEN * XXX: Should these just be silently ignored? */ if ((filemode & (FREAD|FWRITE)) || (filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) || (filemode & (FAPPEND|FCREAT|FTRUNC|FXATTR|FXATTRDIROPEN))) return (set_errno(EINVAL)); } if (startfd == AT_FDCWD) { /* * Regular open() */ startvp = NULL; } else { /* * We're here via openat() */ if (copyin(fname, &startchar, sizeof (char))) return (set_errno(EFAULT)); /* * if startchar is / then startfd is ignored */ if (startchar == '/') startvp = NULL; else { if ((startfp = getf(startfd)) == NULL) return (set_errno(EBADF)); startvp = startfp->f_vnode; VN_HOLD(startvp); releasef(startfd); } } /* * Handle __openattrdirat() requests */ if (filemode & FXATTRDIROPEN) { if (auditing && startvp != NULL) audit_setfsat_path(1); if (error = lookupnameat(fname, seg, FOLLOW, NULLVPP, &vp, startvp)) return (set_errno(error)); if (startvp != NULL) VN_RELE(startvp); startvp = vp; } /* * Do we need to go into extended attribute space? */ if (filemode & FXATTR) { if (startfd == AT_FDCWD) { if (copyin(fname, &startchar, sizeof (char))) return (set_errno(EFAULT)); /* * If startchar == '/' then no extended attributes * are looked up. */ if (startchar == '/') { startvp = NULL; } else { mutex_enter(&p->p_lock); startvp = PTOU(p)->u_cdir; VN_HOLD(startvp); mutex_exit(&p->p_lock); } } /* * Make sure we have a valid extended attribute request. * We must either have a real fd or AT_FDCWD and a relative * pathname. */ if (startvp == NULL) { goto noxattr; } } if (filemode & (FXATTR|FXATTRDIROPEN)) { vattr_t vattr; if (error = pn_get(fname, UIO_USERSPACE, &pn)) { goto out; } /* * In order to access hidden attribute directory the * user must be able to stat() the file */ vattr.va_mask = AT_ALL; if (error = VOP_GETATTR(startvp, &vattr, 0, CRED(), NULL)) { pn_free(&pn); goto out; } if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0 || vfs_has_feature(startvp->v_vfsp, VFSFT_SYSATTR_VIEWS)) { error = VOP_LOOKUP(startvp, "", &sdvp, &pn, (filemode & FXATTRDIROPEN) ? LOOKUP_XATTR : LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED(), NULL, NULL, NULL); } else { error = EINVAL; } /* * For __openattrdirat() use "." as filename to open * as part of vn_openat() */ if (error == 0 && (filemode & FXATTRDIROPEN)) { open_filename = "."; seg = UIO_SYSSPACE; } pn_free(&pn); if (error != 0) goto out; VN_RELE(startvp); startvp = sdvp; } noxattr: if ((filemode & (FREAD|FWRITE|FSEARCH|FEXEC|FXATTRDIROPEN)) != 0) { if ((filemode & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY)) filemode &= ~FNDELAY; error = falloc((vnode_t *)NULL, filemode, &fp, &fd); if (error == 0) { if (auditing && startvp != NULL) audit_setfsat_path(1); /* * Last arg is a don't-care term if * !(filemode & FCREAT). */ error = vn_openat(open_filename, seg, filemode, (int)(createmode & MODEMASK), &vp, CRCREAT, PTOU(curproc)->u_cmask, startvp, fd); if (startvp != NULL) VN_RELE(startvp); if (error == 0) { if ((vp->v_flag & VDUP) == 0) { fp->f_vnode = vp; mutex_exit(&fp->f_tlock); /* * We must now fill in the slot * falloc reserved. */ setf(fd, fp); return (fd); } else { /* * Special handling for /dev/fd. * Give up the file pointer * and dup the indicated file descriptor * (in v_rdev). This is ugly, but I've * seen worse. */ unfalloc(fp); dupfd = getminor(vp->v_rdev); type = vp->v_type; mutex_enter(&vp->v_lock); vp->v_flag &= ~VDUP; mutex_exit(&vp->v_lock); VN_RELE(vp); if (type != VCHR) return (set_errno(EINVAL)); if ((fp = getf(dupfd)) == NULL) { setf(fd, NULL); return (set_errno(EBADF)); } mutex_enter(&fp->f_tlock); fp->f_count++; mutex_exit(&fp->f_tlock); setf(fd, fp); releasef(dupfd); } return (fd); } else { setf(fd, NULL); unfalloc(fp); return (set_errno(error)); } } } else { error = EINVAL; } out: if (startvp != NULL) VN_RELE(startvp); return (set_errno(error)); }
/*ARGSUSED3*/ static int javaexec(vnode_t *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, caddr_t execfile, cred_t *cred, int brand_action) { struct intpdata idata; int error; ssize_t resid; vnode_t *nvp; off_t xoff, xoff_end; char lochdr[LOCHDRSIZ]; struct pathname lookpn; struct pathname resolvepn; char *opath; if (level) return (ENOEXEC); /* no recursion */ /* * Read in the full local file header, and validate * the initial signature. */ if ((error = vn_rdwr(UIO_READ, vp, lochdr, sizeof (lochdr), 0, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0) return (error); if (resid != 0 || strncmp(lochdr, LOCSIG, SIGSIZ) != 0) return (ENOEXEC); /* * Ok, so this -is- a ZIP file, and might even be a JAR file. * Is it a Java executable? */ xoff = sizeof (lochdr) + LOCNAM(lochdr); xoff_end = xoff + LOCEXT(lochdr); while (xoff < xoff_end) { char xfhdr[XFHSIZ]; if ((error = vn_rdwr(UIO_READ, vp, xfhdr, sizeof (xfhdr), xoff, UIO_SYSSPACE, 0, (rlim64_t)0, cred, &resid)) != 0) return (error); if (resid != 0) return (ENOEXEC); if (XFHID(xfhdr) == XFJAVASIG) break; xoff += sizeof (xfhdr) + XFDATASIZ(xfhdr); } if (xoff >= xoff_end) return (ENOEXEC); /* * Note: If we ever make setid execution work, we need to ensure * that we use /dev/fd to avoid the classic setuid shell script * security hole. */ if (setid) return (EACCES); /* * Find and invoke the Java runtime environment on the file */ idata.intp = NULL; idata.intp_name[0] = jexec; idata.intp_arg[0] = jexec_arg; if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &lookpn)) return (error); pn_alloc(&resolvepn); if (error = lookuppn(&lookpn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { pn_free(&resolvepn); pn_free(&lookpn); return (ENOEXEC); } opath = args->pathname; args->pathname = resolvepn.pn_path; /* don't free resolvepn until we are done with args */ pn_free(&lookpn); error = gexec(&nvp, uap, args, &idata, level + 1, execsz, execfile, cred, EBA_NONE); if (!error) { /* * Close this Java executable as the interpreter * will open and close it later on. */ (void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, cred, NULL); } VN_RELE(nvp); args->pathname = opath; pn_free(&resolvepn); return (error); }
static int VMBlockMount(struct vfs *vfsp, // IN: file system to mount struct vnode *vnodep, // IN: Vnode that we are mounting on struct mounta *mntp, // IN: Arguments to mount(2) from user struct cred *credp) // IN: Credentials of caller { VMBlockMountInfo *mip; int ret; Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockMount: entry\n"); /* * These next few checks are done by all other Solaris file systems, so * let's follow their lead. */ ret = secpolicy_fs_mount(credp, vnodep, vfsp); if (ret) { Warning("VMBlockMount: mounting security check failed.\n"); return ret; } if (vnodep->v_type != VDIR) { Warning("VMBlockMount: not mounting on a directory.\n"); return ENOTDIR; } mutex_enter(&vnodep->v_lock); if ((mntp->flags & MS_OVERLAY) == 0 && (vnodep->v_count != 1 || (vnodep->v_flag & VROOT))) { mutex_exit(&vnodep->v_lock); Warning("VMBlockMount: cannot allow unrequested overlay mount.\n"); return EBUSY; } mutex_exit(&vnodep->v_lock); /* * The directory we are redirecting to is specified as the special file * since we have no actual device to mount on. We store that path in the * mount information structure (note that there's another allocation inside * pn_get() so we must pn_free() that path at unmount time). KM_SLEEP * guarantees our memory allocation will succeed (pn_get() uses this flag * too). */ mip = kmem_zalloc(sizeof *mip, KM_SLEEP); ret = pn_get(mntp->spec, (mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &mip->redirectPath); if (ret) { Warning("VMBlockMount: could not obtain redirecting directory.\n"); kmem_free(mip, sizeof *mip); return ret; } /* Do a lookup on the specified path. */ ret = lookupname(mntp->spec, (mntp->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &mip->redirectVnode); if (ret) { Warning("VMBlockMount: could not obtain redirecting directory.\n"); goto error_lookup; } if (mip->redirectVnode->v_type != VDIR) { Warning("VMBlockMount: not redirecting to a directory.\n"); ret = ENOTDIR; goto error; } /* * Initialize our vfs structure. */ vfsp->vfs_vnodecovered = vnodep; vfsp->vfs_flag &= ~VFS_UNMOUNTED; vfsp->vfs_flag |= VMBLOCK_VFS_FLAGS; vfsp->vfs_bsize = PAGESIZE; vfsp->vfs_fstype = vmblockType; vfsp->vfs_bcount = 0; /* If we had mount options, we'd call vfs_setmntopt with vfsp->vfs_mntopts */ /* Locate a unique device minor number for this mount. */ mutex_enter(&vmblockMutex); do { vfsp->vfs_dev = makedevice(vmblockMajor, vmblockMinor); vmblockMinor = (vmblockMinor + 1) & L_MAXMIN32; } while (vfs_devismounted(vfsp->vfs_dev)); mutex_exit(&vmblockMutex); vfs_make_fsid(&vfsp->vfs_fsid, vfsp->vfs_dev, vmblockType); vfsp->vfs_data = (caddr_t)mip; /* * Now create the root vnode of the file system. */ ret = VMBlockVnodeGet(&mip->root, mip->redirectVnode, mip->redirectPath.pn_path, mip->redirectPath.pn_pathlen, NULL, vfsp, TRUE); if (ret) { Warning("VMBlockMount: couldn't create root vnode.\n"); ret = EFAULT; goto error; } VN_HOLD(vfsp->vfs_vnodecovered); return 0; error: /* lookupname() provides a held vnode. */ VN_RELE(mip->redirectVnode); error_lookup: pn_free(&mip->redirectPath); kmem_free(mip, sizeof *mip); return ret; }
static int copen(int startfd, char *fname, int filemode, int createmode) { struct pathname pn; vnode_t *vp, *sdvp; file_t *fp, *startfp; enum vtype type; int error; int fd, dupfd; vnode_t *startvp; proc_t *p = curproc; if (startfd == AT_FDCWD) { /* * Regular open() */ startvp = NULL; } else { /* * We're here via openat() */ char startchar; if (copyin(fname, &startchar, sizeof (char))) return (set_errno(EFAULT)); /* * if startchar is / then startfd is ignored */ if (startchar == '/') startvp = NULL; else { if ((startfp = getf(startfd)) == NULL) return (set_errno(EBADF)); startvp = startfp->f_vnode; VN_HOLD(startvp); releasef(startfd); } } if (filemode & FXATTR) { /* * Make sure we have a valid request. * We must either have a real fd or AT_FDCWD */ if (startfd != AT_FDCWD && startvp == NULL) { error = EINVAL; goto out; } if (error = pn_get(fname, UIO_USERSPACE, &pn)) { goto out; } if (startfd == AT_FDCWD) { mutex_enter(&p->p_lock); startvp = PTOU(p)->u_cdir; VN_HOLD(startvp); mutex_exit(&p->p_lock); } /* * Verify permission to put attributes on file */ if ((VOP_ACCESS(startvp, VREAD, 0, CRED()) != 0) && (VOP_ACCESS(startvp, VWRITE, 0, CRED()) != 0) && (VOP_ACCESS(startvp, VEXEC, 0, CRED()) != 0)) { error = EACCES; pn_free(&pn); goto out; } if ((startvp->v_vfsp->vfs_flag & VFS_XATTR) != 0) { error = VOP_LOOKUP(startvp, "", &sdvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED()); } else { error = EINVAL; } pn_free(&pn); if (error != 0) goto out; VN_RELE(startvp); startvp = sdvp; } if ((filemode & (FREAD|FWRITE)) != 0) { if ((filemode & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY)) filemode &= ~FNDELAY; error = falloc((vnode_t *)NULL, filemode, &fp, &fd); if (error == 0) { #ifdef C2_AUDIT if (audit_active) audit_setfsat_path(1); #endif /* C2_AUDIT */ /* * Last arg is a don't-care term if * !(filemode & FCREAT). */ error = vn_openat(fname, UIO_USERSPACE, filemode, (int)(createmode & MODEMASK), &vp, CRCREAT, u.u_cmask, startvp); if (startvp != NULL) VN_RELE(startvp); if (error == 0) { #ifdef C2_AUDIT if (audit_active) audit_copen(fd, fp, vp); #endif /* C2_AUDIT */ if ((vp->v_flag & VDUP) == 0) { fp->f_vnode = vp; mutex_exit(&fp->f_tlock); /* * We must now fill in the slot * falloc reserved. */ setf(fd, fp); return (fd); } else { /* * Special handling for /dev/fd. * Give up the file pointer * and dup the indicated file descriptor * (in v_rdev). This is ugly, but I've * seen worse. */ unfalloc(fp); dupfd = getminor(vp->v_rdev); type = vp->v_type; mutex_enter(&vp->v_lock); vp->v_flag &= ~VDUP; mutex_exit(&vp->v_lock); VN_RELE(vp); if (type != VCHR) return (set_errno(EINVAL)); if ((fp = getf(dupfd)) == NULL) { setf(fd, NULL); return (set_errno(EBADF)); } mutex_enter(&fp->f_tlock); fp->f_count++; mutex_exit(&fp->f_tlock); setf(fd, fp); releasef(dupfd); } return (fd); } else { setf(fd, NULL); unfalloc(fp); return (set_errno(error)); } } } else { error = EINVAL; } out: if (startvp != NULL) VN_RELE(startvp); return (set_errno(error)); }
/*ARGSUSED*/ static int zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { char *osname; pathname_t spn; int error = 0; uio_seg_t fromspace = (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE; int canwrite; if (mvp->v_type != VDIR) return (ENOTDIR); mutex_enter(&mvp->v_lock); if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * ZFS does not support passing unparsed data in via MS_DATA. * Users should use the MS_OPTIONSTR interface; this means * that all option parsing is already done and the options struct * can be interrogated. */ if ((uap->flags & MS_DATA) && uap->datalen > 0) return (EINVAL); /* * When doing a remount, we simply refresh our temporary properties * according to those options set in the current VFS options. */ if (uap->flags & MS_REMOUNT) { return (zfs_refresh_properties(vfsp)); } /* * Get the objset name (the "special" mount argument). */ if (error = pn_get(uap->spec, fromspace, &spn)) return (error); osname = spn.pn_path; if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) goto out; /* * Refuse to mount a filesystem if we are in a local zone and the * dataset is not visible. */ if (!INGLOBALZONE(curproc) && (!zone_dataset_visible(osname, &canwrite) || !canwrite)) { error = EPERM; goto out; } error = zfs_domount(vfsp, osname, cr); out: pn_free(&spn); return (error); }
/* * Get the "real" XATTR directory associtated with the GFS XATTR directory. * Note: This does NOT take any additional hold on the returned real_vp, * because when this lookup succeeds we save the result in xattr_realvp * and keep that hold until the GFS XATTR directory goes inactive. */ static int xattr_dir_realdir(vnode_t *gfs_dvp, vnode_t **ret_vpp, int flags, cred_t *cr, caller_context_t *ct) { struct pathname pn; char *nm = ""; xattr_dir_t *xattr_dir; vnode_t *realvp; int error; *ret_vpp = NULL; /* * Usually, we've already found the underlying XATTR directory * during some previous lookup and stored it in xattr_realvp. */ mutex_enter(&gfs_dvp->v_lock); xattr_dir = gfs_dvp->v_data; realvp = xattr_dir->xattr_realvp; mutex_exit(&gfs_dvp->v_lock); if (realvp != NULL) { *ret_vpp = realvp; return (0); } /* * Lookup the XATTR dir in the underlying FS, relative to our * "parent", which is the real object for which this GFS XATTR * directory was created. Set the LOOKUP_HAVE_SYSATTR_DIR flag * so that we don't get into an infinite loop with fop_lookup * calling back to xattr_dir_lookup. */ error = pn_get(nm, UIO_SYSSPACE, &pn); if (error != 0) return (error); error = VOP_LOOKUP(gfs_file_parent(gfs_dvp), nm, &realvp, &pn, flags | LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, ct, NULL, NULL); pn_free(&pn); if (error != 0) return (error); /* * Have the real XATTR directory. Save it -- but first * check whether we lost a race doing the lookup. */ mutex_enter(&gfs_dvp->v_lock); xattr_dir = gfs_dvp->v_data; if (xattr_dir->xattr_realvp == NULL) { /* * Note that the hold taken by the VOP_LOOKUP above is * retained from here until xattr_dir_inactive. */ xattr_dir->xattr_realvp = realvp; } else { /* We lost the race. */ VN_RELE(realvp); realvp = xattr_dir->xattr_realvp; } mutex_exit(&gfs_dvp->v_lock); *ret_vpp = realvp; return (0); }
int intpexec( struct vnode *vp, struct execa *uap, struct uarg *args, struct intpdata *idatap, int level, long *execsz, int setid, caddr_t exec_file, struct cred *cred, int brand_action) { _NOTE(ARGUNUSED(brand_action)) vnode_t *nvp; int error = 0; struct intpdata idata; struct pathname intppn; struct pathname resolvepn; char *opath; char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */ int fd = -1; if (level) { /* Can't recurse */ error = ENOEXEC; goto bad; } ASSERT(idatap == (struct intpdata *)NULL); /* * Allocate a buffer to read in the interpreter pathname. */ idata.intp = kmem_alloc(INTPSZ, KM_SLEEP); if (error = getintphead(vp, &idata)) goto fail; /* * Look the new vnode up. */ if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &intppn)) goto fail; pn_alloc(&resolvepn); if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) { pn_free(&resolvepn); pn_free(&intppn); goto fail; } opath = args->pathname; args->pathname = resolvepn.pn_path; /* don't free resolvepn until we are done with args */ pn_free(&intppn); /* * When we're executing a set-uid script resulting in uids * mismatching or when we execute with additional privileges, * we close the "replace script between exec and open by shell" * hole by passing the script as /dev/fd parameter. */ if ((setid & EXECSETID_PRIVS) != 0 || (setid & (EXECSETID_UGIDS|EXECSETID_SETID)) == (EXECSETID_UGIDS|EXECSETID_SETID)) { (void) strcpy(devfd, "/dev/fd/"); if (error = execopen(&vp, &fd)) goto done; numtos(fd, &devfd[8]); args->fname = devfd; } error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred, EBA_NONE); done: VN_RELE(nvp); args->pathname = opath; pn_free(&resolvepn); fail: kmem_free(idata.intp, INTPSZ); if (error && fd != -1) (void) execclose(fd); bad: return (error); }
int xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp) { int error; vnode_t *pvp, *dvp; xattr_fid_t *xfidp; struct pathname pn; char *nm; uint16_t orig_len; *vpp = NULL; if (fidp->fid_len < XATTR_FIDSZ) return (EINVAL); xfidp = (xattr_fid_t *)fidp; orig_len = fidp->fid_len; fidp->fid_len = xfidp->parent_len; error = VFS_VGET(vfsp, &pvp, fidp); fidp->fid_len = orig_len; if (error) return (error); /* * Start by getting the GFS sysattr directory. We might need * to recreate it during the VOP_LOOKUP. */ nm = ""; error = pn_get(nm, UIO_SYSSPACE, &pn); if (error) { VN_RELE(pvp); return (EINVAL); } error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR, rootvp, CRED(), NULL, NULL, NULL); pn_free(&pn); VN_RELE(pvp); if (error) return (error); if (xfidp->dir_offset == 0) { /* * If we were looking for the directory, we're done. */ *vpp = dvp; return (0); } if (xfidp->dir_offset > XATTRDIR_NENTS) { VN_RELE(dvp); return (EINVAL); } nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name; error = pn_get(nm, UIO_SYSSPACE, &pn); if (error) { VN_RELE(dvp); return (EINVAL); } error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL, NULL, NULL); pn_free(&pn); VN_RELE(dvp); return (error); }
/* * Get the XATTR dir for some file or directory. * See vnode.c: fop_lookup() * * Note this only gets the GFS XATTR directory. We'll get the * real XATTR directory later, in xattr_dir_realdir. */ int xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr) { int error = 0; *vpp = NULL; if (dvp->v_type != VDIR && dvp->v_type != VREG) return (EINVAL); mutex_enter(&dvp->v_lock); /* * If we're already in sysattr space, don't allow creation * of another level of sysattrs. */ if (dvp->v_flag & V_SYSATTR) { mutex_exit(&dvp->v_lock); return (EINVAL); } if (dvp->v_xattrdir != NULL) { *vpp = dvp->v_xattrdir; VN_HOLD(*vpp); } else { ulong_t val; int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR; int sysattrs_allowed = 1; /* * We have to drop the lock on dvp. gfs_dir_create will * grab it for a VN_HOLD. */ mutex_exit(&dvp->v_lock); /* * If dvp allows xattr creation, but not sysattr * creation, return the real xattr dir vp. We can't * use the vfs feature mask here because _PC_SATTR_ENABLED * has vnode-level granularity (e.g. .zfs). */ error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL); if (error != 0 || val == 0) sysattrs_allowed = 0; if (!xattrs_allowed && !sysattrs_allowed) return (EINVAL); if (!sysattrs_allowed) { struct pathname pn; char *nm = ""; error = pn_get(nm, UIO_SYSSPACE, &pn); if (error) return (error); error = VOP_LOOKUP(dvp, nm, vpp, &pn, flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL, NULL, NULL); pn_free(&pn); return (error); } /* * Note that we act as if we were given CREATE_XATTR_DIR, * but only for creation of the GFS directory. */ *vpp = gfs_dir_create( sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents, xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb); mutex_enter(&dvp->v_lock); if (dvp->v_xattrdir != NULL) { /* * We lost the race to create the xattr dir. * Destroy this one, use the winner. We can't * just call VN_RELE(*vpp), because the vnode * is only partially initialized. */ gfs_dir_t *dp = (*vpp)->v_data; ASSERT((*vpp)->v_count == 1); vn_free(*vpp); mutex_destroy(&dp->gfsd_lock); kmem_free(dp->gfsd_static, dp->gfsd_nstatic * sizeof (gfs_dirent_t)); kmem_free(dp, dp->gfsd_file.gfs_size); /* * There is an implied VN_HOLD(dvp) here. We should * be doing a VN_RELE(dvp) to clean up the reference * from *vpp, and then a VN_HOLD(dvp) for the new * reference. Instead, we just leave the count alone. */ *vpp = dvp->v_xattrdir; VN_HOLD(*vpp); } else { (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR); dvp->v_xattrdir = *vpp; } } mutex_exit(&dvp->v_lock); return (error); }
static int VMBlockIoctl(struct vnode *vp, // IN: Vnode of file to operate on int cmd, // IN: Requested command from user intptr_t arg, // IN: Arguments for command int flag, // IN: File pointer flags and data model struct cred *cr, // IN: Credentials of caller int *rvalp // OUT: Return value on success #if OS_VFS_VERSION >= 5 , caller_context_t *ctx // IN: Caller's context #endif ) { VMBlockMountInfo *mip; int ret; Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockIoctl: entry\n"); mip = VPTOMIP(vp); if (vp != mip->root) { return ENOTSUP; } if (rvalp) { *rvalp = 0; } switch (cmd) { case VMBLOCK_ADD_FILEBLOCK: case VMBLOCK_DEL_FILEBLOCK: { struct pathname pn; ret = pn_get((char *)arg, UIO_USERSPACE, &pn); if (ret) { goto out; } /* Remove all trailing path separators. */ while (pn.pn_pathlen > 0 && pn.pn_path[pn.pn_pathlen - 1] == '/') { pn.pn_path[pn.pn_pathlen - 1] = '\0'; pn.pn_pathlen--; } ret = cmd == VMBLOCK_ADD_FILEBLOCK ? BlockAddFileBlock(pn.pn_path, curthread) : BlockRemoveFileBlock(pn.pn_path, curthread); pn_free(&pn); break; } #ifdef VMX86_DEVEL case VMBLOCK_LIST_FILEBLOCKS: BlockListFileBlocks(); ret = 0; break; #endif default: Warning("VMBlockIoctl: unknown command (%d) received.\n", cmd); return ENOTSUP; } out: return ret; }
int dogetcwd(char *buf, size_t buflen) { int ret; vnode_t *vp; vnode_t *compvp; refstr_t *cwd, *oldcwd; const char *value; pathname_t rpnp, pnp; proc_t *p = curproc; /* * Check to see if there is a cached version of the cwd. If so, lookup * the cached value and make sure it is the same vnode. */ mutex_enter(&p->p_lock); if ((cwd = PTOU(p)->u_cwd) != NULL) refstr_hold(cwd); vp = PTOU(p)->u_cdir; VN_HOLD(vp); mutex_exit(&p->p_lock); /* * Make sure we have permission to access the current directory. */ if ((ret = VOP_ACCESS(vp, VEXEC, 0, CRED(), NULL)) != 0) { if (cwd != NULL) refstr_rele(cwd); VN_RELE(vp); return (ret); } if (cwd) { value = refstr_value(cwd); if ((ret = pn_get((char *)value, UIO_SYSSPACE, &pnp)) != 0) { refstr_rele(cwd); VN_RELE(vp); return (ret); } pn_alloc(&rpnp); if (lookuppn(&pnp, &rpnp, NO_FOLLOW, NULL, &compvp) == 0) { if (VN_CMP(vp, compvp) && strcmp(value, rpnp.pn_path) == 0) { VN_RELE(compvp); VN_RELE(vp); pn_free(&pnp); pn_free(&rpnp); if (strlen(value) + 1 > buflen) { refstr_rele(cwd); return (ENAMETOOLONG); } bcopy(value, buf, strlen(value) + 1); refstr_rele(cwd); return (0); } VN_RELE(compvp); } pn_free(&rpnp); pn_free(&pnp); refstr_rele(cwd); } ret = vnodetopath_common(NULL, vp, buf, buflen, CRED(), LOOKUP_CHECKREAD); VN_RELE(vp); /* * Store the new cwd and replace the existing cached copy. */ if (ret == 0) cwd = refstr_alloc(buf); else cwd = NULL; mutex_enter(&p->p_lock); oldcwd = PTOU(p)->u_cwd; PTOU(p)->u_cwd = cwd; mutex_exit(&p->p_lock); if (oldcwd) refstr_rele(oldcwd); return (ret); }
static int zfs_vfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context) { char *osname = NULL; size_t osnamelen = 0; int error = 0; int canwrite; /* * Get the objset name (the "special" mount argument). * The filesystem that we mount as root is defined in the * "zfs-bootfs" property. */ if (data) { user_addr_t fspec = USER_ADDR_NULL; #ifndef __APPLE__ if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bootpath) != DDI_SUCCESS) return (EIO); error = parse_bootpath(zfs_bootpath, rootfs.bo_name); ddi_prop_free(zfs_bootpath); #endif osname = kmem_alloc(MAXPATHLEN, KM_SLEEP); if (vfs_context_is64bit(context)) { if ( (error = copyin(data, (caddr_t)&fspec, sizeof(fspec))) ) goto out; } else { #ifdef ZFS_LEOPARD_ONLY char *tmp; #else user32_addr_t tmp; #endif if ( (error = copyin(data, (caddr_t)&tmp, sizeof(tmp))) ) goto out; /* munge into LP64 addr */ fspec = CAST_USER_ADDR_T(tmp); } if ( (error = copyinstr(fspec, osname, MAXPATHLEN, &osnamelen)) ) goto out; } #if 0 if (mvp->v_type != VDIR) return (ENOTDIR); mutex_enter(&mvp->v_lock); if ((uap->flags & MS_REMOUNT) == 0 && (uap->flags & MS_OVERLAY) == 0 && (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { mutex_exit(&mvp->v_lock); return (EBUSY); } mutex_exit(&mvp->v_lock); /* * ZFS does not support passing unparsed data in via MS_DATA. * Users should use the MS_OPTIONSTR interface; this means * that all option parsing is already done and the options struct * can be interrogated. */ if ((uap->flags & MS_DATA) && uap->datalen > 0) return (EINVAL); /* * Get the objset name (the "special" mount argument). */ if (error = pn_get(uap->spec, fromspace, &spn)) return (error); osname = spn.pn_path; #endif /* * Check for mount privilege? * * If we don't have privilege then see if * we have local permission to allow it */ #ifndef __APPLE__ error = secpolicy_fs_mount(cr, mvp, vfsp); if (error) { error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr); if (error == 0) { vattr_t vattr; /* * Make sure user is the owner of the mount point * or has sufficient privileges. */ vattr.va_mask = AT_UID; if (error = VOP_GETATTR(mvp, &vattr, 0, cr)) { goto out; } if (error = secpolicy_vnode_owner(cr, vattr.va_uid)) { goto out; } if (error = VOP_ACCESS(mvp, VWRITE, 0, cr)) { goto out; } secpolicy_fs_mount_clearopts(cr, vfsp); } else { goto out; } } #endif error = zfs_domount(mp, 0, osname, context); if (error) printf("zfs_vfs_mount: error %d\n", error); if (error == 0) { zfsvfs_t *zfsvfs = NULL; /* Make the Finder treat sub file systems just like a folder */ if (strpbrk(osname, "/")) vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_DONTBROWSE)); /* Indicate to VFS that we support ACLs. */ vfs_setextendedsecurity(mp); /* Advisory locking should be handled at the VFS layer */ vfs_setlocklocal(mp); /* * Mac OS X needs a file system modify time * * We use the mtime of the "com.apple.system.mtime" * extended attribute, which is associated with the * file system root directory. * * Here we need to take a ref on z_mtime_vp to keep it around. * If the attribute isn't there, attempt to create it. */ zfsvfs = vfs_fsprivate(mp); if (zfsvfs->z_mtime_vp == NULL) { struct vnode * rvp; struct vnode *xdvp = NULLVP; struct vnode *xvp = NULLVP; znode_t *rootzp; timestruc_t modify_time; cred_t *cr; timestruc_t now; int flag; int result; if (zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp) != 0) { goto out; } rvp = ZTOV(rootzp); cr = (cred_t *)vfs_context_ucred(context); /* Grab the hidden attribute directory vnode. */ result = zfs_get_xattrdir(rootzp, &xdvp, cr, CREATE_XATTR_DIR); vnode_put(rvp); /* all done with root vnode */ rvp = NULL; if (result) { goto out; } /* * HACK - workaround missing vnode_setnoflush() KPI... * * We tag zfsvfs so that zfs_attach_vnode() can then set * vnfs_marksystem when the vnode gets created. */ zfsvfs->z_last_unmount_time = 0xBADC0DE; zfsvfs->z_last_mtime_synced = VTOZ(xdvp)->z_id; flag = vfs_isrdonly(mp) ? 0 : ZEXISTS; /* Lookup or create the named attribute. */ if ( zfs_obtain_xattr(VTOZ(xdvp), ZFS_MTIME_XATTR, S_IRUSR | S_IWUSR, cr, &xvp, flag) ) { zfsvfs->z_last_unmount_time = 0; zfsvfs->z_last_mtime_synced = 0; vnode_put(xdvp); goto out; } gethrestime(&now); ZFS_TIME_ENCODE(&now, VTOZ(xvp)->z_phys->zp_mtime); vnode_put(xdvp); vnode_ref(xvp); zfsvfs->z_mtime_vp = xvp; ZFS_TIME_DECODE(&modify_time, VTOZ(xvp)->z_phys->zp_mtime); zfsvfs->z_last_unmount_time = modify_time.tv_sec; zfsvfs->z_last_mtime_synced = modify_time.tv_sec; /* * Keep this referenced vnode from impeding an unmount. * * XXX vnode_setnoflush() is MIA from KPI (see workaround above). */ #if 0 vnode_setnoflush(xvp); #endif vnode_put(xvp); } } out: if (osname) { kmem_free(osname, MAXPATHLEN); } return (error); }