int smb_vop_mkdir(vnode_t *dvp, char *name, smb_attr_t *attr, vnode_t **vpp, int flags, cred_t *cr, vsecattr_t *vsap) { int error; int option_flags = 0; xvattr_t xvattr; vattr_t *vap; char *np = name; char namebuf[MAXNAMELEN]; if (flags & SMB_IGNORE_CASE) option_flags = FIGNORECASE; attr->sa_vattr.va_mask = 0; if (vfs_has_feature(dvp->v_vfsp, VFSFT_XVATTR)) { smb_vop_setup_xvattr(attr, &xvattr); vap = &xvattr.xva_vattr; } else { smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); vap = &attr->sa_vattr; } if (flags & SMB_CATIA) { np = smb_vop_catia_v5tov4(name, namebuf, sizeof (namebuf)); if (strchr(np, '/') != NULL) return (EILSEQ); } error = VOP_MKDIR(dvp, np, vap, vpp, cr, &smb_ct, option_flags, vsap); return (error); }
/* * smb_vop_setattr() * * smb_fsop_setattr()/smb_vop_setattr() should always be used instead of * VOP_SETATTR() when calling from the CIFS service, due to special processing * for streams files. * * Streams have a size but otherwise do not have separate attributes from * the (unnamed stream) file, i.e., the security and ownership of the file * applies to the stream. In contrast, extended attribute files, which are * used to implement streams, are independent objects with their own * attributes. * * For compatibility with streams, we set the size on the extended attribute * file and apply other attributes to the (unnamed stream) file. The one * exception is that the UID and GID can be set on the stream by passing a * NULL unnamed_vp, which allows callers to synchronize stream ownership * with the (unnamed stream) file. */ int smb_vop_setattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *attr, int flags, cred_t *cr) { int error = 0; int at_size = 0; vnode_t *use_vp; xvattr_t xvattr; vattr_t *vap; if (attr->sa_mask & SMB_AT_DOSATTR) { attr->sa_dosattr &= (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); } if (unnamed_vp) { use_vp = unnamed_vp; if (attr->sa_mask & SMB_AT_SIZE) { at_size = 1; attr->sa_mask &= ~SMB_AT_SIZE; } } else { use_vp = vp; } /* * The caller should not be setting sa_vattr.va_mask, * but rather sa_mask. */ attr->sa_vattr.va_mask = 0; if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { smb_vop_setup_xvattr(attr, &xvattr); vap = &xvattr.xva_vattr; } else { smb_sa_to_va_mask(attr->sa_mask, &attr->sa_vattr.va_mask); vap = &attr->sa_vattr; } if ((error = VOP_SETATTR(use_vp, vap, flags, cr, &smb_ct)) != 0) return (error); if (at_size) { attr->sa_vattr.va_mask = AT_SIZE; error = VOP_SETATTR(vp, &attr->sa_vattr, flags, kcred, &smb_ct); } return (error); }
/* * smb_vop_readdir() * * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. * The directory entries are returned in an fs-independent format by the * underlying file system. That is, the "page" of information returned is * not literally stored on-disk in the format returned. * If the file system supports extended directory entries (has features * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be * filled with edirent_t structures, instead of dirent64_t structures. * If the file system supports access based enumeration (abe), set * V_RDDIR_ACCFILTER to filter directory entries based on user cred. */ int smb_vop_readdir(vnode_t *vp, uint32_t offset, void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr) { int error = 0; int flags = 0; int rdirent_size; struct uio auio; struct iovec aiov; if (vp->v_type != VDIR) return (ENOTDIR); if (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) { flags |= V_RDDIR_ENTFLAGS; rdirent_size = sizeof (edirent_t); } else { rdirent_size = sizeof (dirent64_t); } if (*count < rdirent_size) return (EINVAL); if (rddir_flag & SMB_ABE) flags |= V_RDDIR_ACCFILTER; aiov.iov_base = buf; aiov.iov_len = *count; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_loffset = (uint64_t)offset; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = *count; auio.uio_fmode = 0; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); if (error == 0) *count = *count - auio.uio_resid; return (error); }
/* ARGSUSED */ static int xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct) { int error = 0; char *buf; char *domain; uint32_t rid; ssize_t size = uiop->uio_resid; nvlist_t *nvp; nvpair_t *pair = NULL; vnode_t *ppvp; xvattr_t xvattr; xoptattr_t *xoap = NULL; /* Pointer to optional attributes */ if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0) return (EINVAL); /* * Validate file offset and size. */ if (uiop->uio_loffset < (offset_t)0) return (EINVAL); if (size == 0) return (EINVAL); xva_init(&xvattr); if ((xoap = xva_getxoptattr(&xvattr)) == NULL) { return (EINVAL); } /* * Copy and unpack the nvlist */ buf = kmem_alloc(size, KM_SLEEP); if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) { return (EFAULT); } if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) { kmem_free(buf, size); uiop->uio_resid = size; return (EINVAL); } kmem_free(buf, size); /* * Fasttrack empty writes (nvlist with no nvpairs) */ if (nvlist_next_nvpair(nvp, NULL) == 0) return (0); ppvp = gfs_file_parent(gfs_file_parent(vp)); while (pair = nvlist_next_nvpair(nvp, pair)) { data_type_t type; f_attr_t attr; boolean_t value; uint64_t *time, *times; uint_t elem, nelems; nvlist_t *nvp_sid; uint8_t *scanstamp; /* * Validate the name and type of each attribute. * Log any unknown names and continue. This will * help if additional attributes are added later. */ type = nvpair_type(pair); if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) { cmn_err(CE_WARN, "Unknown attribute %s", nvpair_name(pair)); continue; } /* * Verify nvlist type matches required type and view is OK */ if (type != attr_to_data_type(attr) || (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) { nvlist_free(nvp); return (EINVAL); } /* * For OWNERSID/GROUPSID make sure the target * file system support ephemeral ID's */ if ((attr == F_OWNERSID || attr == F_GROUPSID) && (!(vp->v_vfsp->vfs_flag & VFS_XID))) { nvlist_free(nvp); return (EINVAL); } /* * Retrieve data from nvpair */ switch (type) { case DATA_TYPE_BOOLEAN_VALUE: if (nvpair_value_boolean_value(pair, &value)) { nvlist_free(nvp); return (EINVAL); } break; case DATA_TYPE_UINT64_ARRAY: if (nvpair_value_uint64_array(pair, ×, &nelems)) { nvlist_free(nvp); return (EINVAL); } break; case DATA_TYPE_NVLIST: if (nvpair_value_nvlist(pair, &nvp_sid)) { nvlist_free(nvp); return (EINVAL); } break; case DATA_TYPE_UINT8_ARRAY: if (nvpair_value_uint8_array(pair, &scanstamp, &nelems)) { nvlist_free(nvp); return (EINVAL); } break; default: nvlist_free(nvp); return (EINVAL); } switch (attr) { /* * If we have several similar optional attributes to * process then we should do it all together here so that * xoap and the requested bitmap can be set in one place. */ case F_READONLY: XVA_SET_REQ(&xvattr, XAT_READONLY); xoap->xoa_readonly = value; break; case F_HIDDEN: XVA_SET_REQ(&xvattr, XAT_HIDDEN); xoap->xoa_hidden = value; break; case F_SYSTEM: XVA_SET_REQ(&xvattr, XAT_SYSTEM); xoap->xoa_system = value; break; case F_ARCHIVE: XVA_SET_REQ(&xvattr, XAT_ARCHIVE); xoap->xoa_archive = value; break; case F_IMMUTABLE: XVA_SET_REQ(&xvattr, XAT_IMMUTABLE); xoap->xoa_immutable = value; break; case F_NOUNLINK: XVA_SET_REQ(&xvattr, XAT_NOUNLINK); xoap->xoa_nounlink = value; break; case F_APPENDONLY: XVA_SET_REQ(&xvattr, XAT_APPENDONLY); xoap->xoa_appendonly = value; break; case F_NODUMP: XVA_SET_REQ(&xvattr, XAT_NODUMP); xoap->xoa_nodump = value; break; case F_AV_QUARANTINED: XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED); xoap->xoa_av_quarantined = value; break; case F_AV_MODIFIED: XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED); xoap->xoa_av_modified = value; break; case F_CRTIME: XVA_SET_REQ(&xvattr, XAT_CREATETIME); time = (uint64_t *)&(xoap->xoa_createtime); for (elem = 0; elem < nelems; elem++) *time++ = times[elem]; break; case F_OWNERSID: case F_GROUPSID: if (nvlist_lookup_string(nvp_sid, SID_DOMAIN, &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID, &rid)) { nvlist_free(nvp); return (EINVAL); } /* * Now map domain+rid to ephemeral id's * * If mapping fails, then the uid/gid will * be set to UID_NOBODY by Winchester. */ if (attr == F_OWNERSID) { (void) kidmap_getuidbysid(crgetzone(cr), domain, rid, &xvattr.xva_vattr.va_uid); xvattr.xva_vattr.va_mask |= AT_UID; } else { (void) kidmap_getgidbysid(crgetzone(cr), domain, rid, &xvattr.xva_vattr.va_gid); xvattr.xva_vattr.va_mask |= AT_GID; } break; case F_AV_SCANSTAMP: if (ppvp->v_type == VREG) { XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP); (void) memcpy(xoap->xoa_av_scanstamp, scanstamp, nelems); } else { nvlist_free(nvp); return (EINVAL); } break; case F_REPARSE: XVA_SET_REQ(&xvattr, XAT_REPARSE); xoap->xoa_reparse = value; break; case F_OFFLINE: XVA_SET_REQ(&xvattr, XAT_OFFLINE); xoap->xoa_offline = value; break; case F_SPARSE: XVA_SET_REQ(&xvattr, XAT_SPARSE); xoap->xoa_sparse = value; break; default: break; } } ppvp = gfs_file_parent(gfs_file_parent(vp)); error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct); if (error) uiop->uio_resid = size; nvlist_free(nvp); return (error); }
/* ARGSUSED */ int fs_pathconf( vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr, caller_context_t *ct) { register ulong_t val; register int error = 0; struct statvfs64 vfsbuf; switch (cmd) { case _PC_LINK_MAX: val = MAXLINK; break; case _PC_MAX_CANON: val = MAX_CANON; break; case _PC_MAX_INPUT: val = MAX_INPUT; break; case _PC_NAME_MAX: bzero(&vfsbuf, sizeof (vfsbuf)); if (error = VFS_STATVFS(vp->v_vfsp, &vfsbuf)) break; val = vfsbuf.f_namemax; break; case _PC_PATH_MAX: case _PC_SYMLINK_MAX: val = MAXPATHLEN; break; case _PC_PIPE_BUF: val = PIPE_BUF; break; case _PC_NO_TRUNC: if (vp->v_vfsp->vfs_flag & VFS_NOTRUNC) val = 1; /* NOTRUNC is enabled for vp */ else val = (ulong_t)-1; break; case _PC_VDISABLE: val = _POSIX_VDISABLE; break; case _PC_CHOWN_RESTRICTED: if (rstchown) val = rstchown; /* chown restricted enabled */ else val = (ulong_t)-1; break; case _PC_FILESIZEBITS: /* * If ever we come here it means that underlying file system * does not recognise the command and therefore this * configurable limit cannot be determined. We return -1 * and don't change errno. */ val = (ulong_t)-1; /* large file support */ break; case _PC_ACL_ENABLED: val = 0; break; case _PC_CASE_BEHAVIOR: val = _CASE_SENSITIVE; if (vfs_has_feature(vp->v_vfsp, VFSFT_CASEINSENSITIVE) == 1) val |= _CASE_INSENSITIVE; if (vfs_has_feature(vp->v_vfsp, VFSFT_NOCASESENSITIVE) == 1) val &= ~_CASE_SENSITIVE; break; case _PC_SATTR_ENABLED: case _PC_SATTR_EXISTS: val = 0; break; case _PC_ACCESS_FILTERING: val = 0; break; default: error = EINVAL; break; } if (error == 0) *valp = val; return (error); }
/* * gfs_dir_lookup() * * Looks up the given name in the directory and returns the corresponding * vnode, if found. * * First, we search statically defined entries, if any, with a call to * gfs_dir_lookup_static(). If no static entry is found, and we have * a callback function we try a dynamic lookup via gfs_dir_lookup_dynamic(). * * This function returns 0 on success, non-zero on error. */ int gfs_dir_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, cred_t *cr, int flags, int *direntflags, pathname_t *realpnp) { gfs_dir_t *dp = dvp->v_data; boolean_t casecheck; vnode_t *dynvp = NULL; vnode_t *vp = NULL; int (*compare)(const char *, const char *); int error, idx; ASSERT(dvp->v_type == VDIR); if (gfs_lookup_dot(vpp, dvp, dp->gfsd_file.gfs_parent, nm) == 0) return (0); casecheck = (flags & FIGNORECASE) != 0 && direntflags != NULL; if (vfs_has_feature(dvp->v_vfsp, VFSFT_NOCASESENSITIVE) || (flags & FIGNORECASE)) compare = strcasecmp; else compare = strcmp; gfs_dir_lock(dp); error = gfs_dir_lookup_static(compare, dp, nm, dvp, &idx, &vp, realpnp); if (vp && casecheck) { gfs_dirent_t *ge; int i; for (i = idx + 1; i < dp->gfsd_nstatic; i++) { ge = &dp->gfsd_static[i]; if (strcasecmp(ge->gfse_name, nm) == 0) { *direntflags |= ED_CASE_CONFLICT; goto out; } } } if ((error || casecheck) && dp->gfsd_lookup) error = gfs_dir_lookup_dynamic(dp->gfsd_lookup, dp, nm, dvp, &dynvp, cr, flags, direntflags, vp ? NULL : realpnp); if (vp && dynvp) { /* static and dynamic entries are case-insensitive conflict */ ASSERT(casecheck); *direntflags |= ED_CASE_CONFLICT; VN_RELE(dynvp); } else if (vp == NULL) { vp = dynvp; } else if (error == ENOENT) { error = 0; } else if (error) { VN_RELE(vp); vp = NULL; } out: gfs_dir_unlock(dp); *vpp = vp; return (error); }
/* * Always set ACL support because the VFS will fake ACLs for file systems * that don't support them. * * Some flags are dependent on the typename, which is also set up here. * File system types are hardcoded in uts/common/os/vfs_conf.c. */ static void smb_tree_get_flags(const smb_share_t *si, vfs_t *vfsp, smb_tree_t *tree) { typedef struct smb_mtype { char *mt_name; size_t mt_namelen; uint32_t mt_flags; } smb_mtype_t; static smb_mtype_t smb_mtype[] = { { "zfs", 3, SMB_TREE_UNICODE_ON_DISK }, { "ufs", 3, SMB_TREE_UNICODE_ON_DISK }, { "nfs", 3, SMB_TREE_NFS_MOUNTED }, { "tmpfs", 5, SMB_TREE_NO_EXPORT } }; smb_mtype_t *mtype; char *name; uint32_t flags = SMB_TREE_SUPPORTS_ACLS; int i; if (si->shr_flags & SMB_SHRF_CATIA) flags |= SMB_TREE_CATIA; if (si->shr_flags & SMB_SHRF_ABE) flags |= SMB_TREE_ABE; if (vfsp->vfs_flag & VFS_RDONLY) flags |= SMB_TREE_READONLY; if (vfsp->vfs_flag & VFS_XATTR) flags |= SMB_TREE_STREAMS; if (vfs_optionisset(vfsp, MNTOPT_NOATIME, NULL)) flags |= SMB_TREE_NO_ATIME; name = vfssw[vfsp->vfs_fstype].vsw_name; for (i = 0; i < sizeof (smb_mtype) / sizeof (smb_mtype[0]); ++i) { mtype = &smb_mtype[i]; if (strncasecmp(name, mtype->mt_name, mtype->mt_namelen) == 0) flags |= mtype->mt_flags; } (void) strlcpy(tree->t_typename, name, SMB_TYPENAMELEN); (void) smb_strupr((char *)tree->t_typename); if (vfs_has_feature(vfsp, VFSFT_XVATTR)) flags |= SMB_TREE_XVATTR; if (vfs_has_feature(vfsp, VFSFT_CASEINSENSITIVE)) flags |= SMB_TREE_CASEINSENSITIVE; if (vfs_has_feature(vfsp, VFSFT_NOCASESENSITIVE)) flags |= SMB_TREE_NO_CASESENSITIVE; if (vfs_has_feature(vfsp, VFSFT_DIRENTFLAGS)) flags |= SMB_TREE_DIRENTFLAGS; if (vfs_has_feature(vfsp, VFSFT_ACLONCREATE)) flags |= SMB_TREE_ACLONCREATE; if (vfs_has_feature(vfsp, VFSFT_ACEMASKONACCESS)) flags |= SMB_TREE_ACEMASKONACCESS; DTRACE_PROBE2(smb__tree__flags, uint32_t, flags, char *, name); tree->t_flags = flags; }
/* * smb_vop_getattr() * * smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS * service (instead of calling VOP_GETATTR directly) to retrieve attributes * due to special processing needed for streams files. * * All attributes are retrieved. * * When vp denotes a named stream, then unnamed_vp should be passed in (denoting * the corresponding unnamed stream). * A named stream's attributes (as far as CIFS is concerned) are those of the * unnamed stream (minus the size attribute, and the type), plus the size of * the named stream, and a type value of VREG. * Although the file system may store other attributes with the named stream, * these should not be used by CIFS for any purpose. * * File systems without VFSFT_XVATTR do not support DOS attributes or create * time (crtime). In this case the mtime is used as the crtime. * Likewise if VOP_GETATTR doesn't return any system attributes the dosattr * is 0 and the mtime is used as the crtime. */ int smb_vop_getattr(vnode_t *vp, vnode_t *unnamed_vp, smb_attr_t *ret_attr, int flags, cred_t *cr) { int error; vnode_t *use_vp; smb_attr_t tmp_attr; xvattr_t tmp_xvattr; xoptattr_t *xoap = NULL; if (unnamed_vp) use_vp = unnamed_vp; else use_vp = vp; if (vfs_has_feature(use_vp->v_vfsp, VFSFT_XVATTR)) { xva_init(&tmp_xvattr); xoap = xva_getxoptattr(&tmp_xvattr); ASSERT(xoap); smb_sa_to_va_mask(ret_attr->sa_mask, &tmp_xvattr.xva_vattr.va_mask); XVA_SET_REQ(&tmp_xvattr, XAT_READONLY); XVA_SET_REQ(&tmp_xvattr, XAT_HIDDEN); XVA_SET_REQ(&tmp_xvattr, XAT_SYSTEM); XVA_SET_REQ(&tmp_xvattr, XAT_ARCHIVE); XVA_SET_REQ(&tmp_xvattr, XAT_CREATETIME); error = VOP_GETATTR(use_vp, &tmp_xvattr.xva_vattr, flags, cr, &smb_ct); if (error != 0) return (error); ret_attr->sa_vattr = tmp_xvattr.xva_vattr; ret_attr->sa_dosattr = 0; if (tmp_xvattr.xva_vattr.va_mask & AT_XVATTR) { xoap = xva_getxoptattr(&tmp_xvattr); ASSERT(xoap); if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_READONLY)) && (xoap->xoa_readonly)) { ret_attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; } if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_HIDDEN)) && (xoap->xoa_hidden)) { ret_attr->sa_dosattr |= FILE_ATTRIBUTE_HIDDEN; } if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_SYSTEM)) && (xoap->xoa_system)) { ret_attr->sa_dosattr |= FILE_ATTRIBUTE_SYSTEM; } if ((XVA_ISSET_RTN(&tmp_xvattr, XAT_ARCHIVE)) && (xoap->xoa_archive)) { ret_attr->sa_dosattr |= FILE_ATTRIBUTE_ARCHIVE; } ret_attr->sa_crtime = xoap->xoa_createtime; } else { ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime; } } else { /* * Support for file systems without VFSFT_XVATTR */ smb_sa_to_va_mask(ret_attr->sa_mask, &ret_attr->sa_vattr.va_mask); error = VOP_GETATTR(use_vp, &ret_attr->sa_vattr, flags, cr, &smb_ct); if (error != 0) return (error); ret_attr->sa_dosattr = 0; ret_attr->sa_crtime = ret_attr->sa_vattr.va_mtime; } if (unnamed_vp) { ret_attr->sa_vattr.va_type = VREG; if (ret_attr->sa_mask & (SMB_AT_SIZE | SMB_AT_NBLOCKS)) { tmp_attr.sa_vattr.va_mask = AT_SIZE | AT_NBLOCKS; error = VOP_GETATTR(vp, &tmp_attr.sa_vattr, flags, cr, &smb_ct); if (error != 0) return (error); ret_attr->sa_vattr.va_size = tmp_attr.sa_vattr.va_size; ret_attr->sa_vattr.va_nblocks = tmp_attr.sa_vattr.va_nblocks; } } if (ret_attr->sa_vattr.va_type == VDIR) ret_attr->sa_dosattr |= FILE_ATTRIBUTE_DIRECTORY; return (error); }
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)); }
/* * smb_odir_create * Allocate and populate an odir obect and add it to the tree's list. */ static uint16_t smb_odir_create(smb_request_t *sr, smb_node_t *dnode, char *pattern, uint16_t sattr, cred_t *cr) { smb_odir_t *od; smb_tree_t *tree; uint16_t odid; ASSERT(sr); ASSERT(sr->sr_magic == SMB_REQ_MAGIC); ASSERT(sr->tid_tree); ASSERT(sr->tid_tree->t_magic == SMB_TREE_MAGIC); ASSERT(dnode); ASSERT(dnode->n_magic == SMB_NODE_MAGIC); tree = sr->tid_tree; if (smb_idpool_alloc(&tree->t_odid_pool, &odid)) { smbsr_error(sr, NT_STATUS_TOO_MANY_OPENED_FILES, ERRDOS, ERROR_TOO_MANY_OPEN_FILES); return (0); } od = kmem_cache_alloc(smb_cache_odir, KM_SLEEP); bzero(od, sizeof (smb_odir_t)); mutex_init(&od->d_mutex, NULL, MUTEX_DEFAULT, NULL); od->d_refcnt = 0; od->d_state = SMB_ODIR_STATE_OPEN; od->d_magic = SMB_ODIR_MAGIC; od->d_opened_by_pid = sr->smb_pid; od->d_session = tree->t_session; od->d_cred = cr; /* * grab a ref for od->d_user * released in smb_odir_delete() */ smb_user_hold_internal(sr->uid_user); od->d_user = sr->uid_user; od->d_tree = tree; od->d_dnode = dnode; smb_node_ref(dnode); od->d_odid = odid; od->d_sattr = sattr; (void) strlcpy(od->d_pattern, pattern, sizeof (od->d_pattern)); od->d_flags = 0; if (smb_contains_wildcards(od->d_pattern)) od->d_flags |= SMB_ODIR_FLAG_WILDCARDS; if (vfs_has_feature(dnode->vp->v_vfsp, VFSFT_DIRENTFLAGS)) od->d_flags |= SMB_ODIR_FLAG_EDIRENT; if (smb_tree_has_feature(tree, SMB_TREE_CASEINSENSITIVE)) od->d_flags |= SMB_ODIR_FLAG_IGNORE_CASE; if (smb_tree_has_feature(tree, SMB_TREE_SHORTNAMES)) od->d_flags |= SMB_ODIR_FLAG_SHORTNAMES; if (SMB_TREE_SUPPORTS_CATIA(sr)) od->d_flags |= SMB_ODIR_FLAG_CATIA; if (SMB_TREE_SUPPORTS_ABE(sr)) od->d_flags |= SMB_ODIR_FLAG_ABE; if (dnode->flags & NODE_XATTR_DIR) od->d_flags |= SMB_ODIR_FLAG_XATTR; od->d_eof = B_FALSE; smb_llist_enter(&tree->t_odir_list, RW_WRITER); smb_llist_insert_tail(&tree->t_odir_list, od); smb_llist_exit(&tree->t_odir_list); atomic_inc_32(&tree->t_session->s_dir_cnt); return (odid); }