/* * FileFsControlInformation */ uint32_t smb2_qfs_control(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) { /* * Strange error per. [MS-FSCC 2.5.2] * which means quotas not supported. */ return (NT_STATUS_VOLUME_NOT_UPGRADED); } (void) smb_mbc_encodef( &sr->raw_data, "qqqqqll", 0, /* free space start filtering - MUST be 0 */ 0, /* free space threshold - MUST be 0 */ 0, /* free space stop filtering - MUST be 0 */ SMB_QUOTA_UNLIMITED, /* default quota threshold */ SMB_QUOTA_UNLIMITED, /* default quota limit */ FILE_VC_QUOTA_ENFORCE, /* fs control flag */ 0); /* pad bytes */ return (0); }
/* * Similar to smb_nt_transact_set_quota() */ uint32_t smb2_setinfo_quota(smb_request_t *sr, smb_setinfo_t *si) { char *root_path; uint32_t status = NT_STATUS_SUCCESS; smb_ofile_t *ofile = sr->fid_ofile; smb_node_t *tnode; smb_quota_set_t request; uint32_t reply; list_t *quota_list; bzero(&request, sizeof (smb_quota_set_t)); if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) return (NT_STATUS_NOT_SUPPORTED); if (!smb_user_is_admin(sr->uid_user)) return (NT_STATUS_ACCESS_DENIED); if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) return (NT_STATUS_ACCESS_DENIED); tnode = sr->tid_tree->t_snode; root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0) { smbsr_release_file(sr); kmem_free(root_path, MAXPATHLEN); return (NT_STATUS_INVALID_PARAMETER); } quota_list = &request.qs_quota_list; list_create(quota_list, sizeof (smb_quota_t), offsetof(smb_quota_t, q_list_node)); status = smb_quota_decode_quotas(&si->si_data, quota_list); if (status == NT_STATUS_SUCCESS) { request.qs_root_path = root_path; if (smb_quota_set(sr->sr_server, &request, &reply) != 0) { status = NT_STATUS_INTERNAL_ERROR; } else { status = reply; xdr_free(xdr_uint32_t, (char *)&reply); } } kmem_free(root_path, MAXPATHLEN); smb_quota_free_quotas(&request.qs_quota_list); smbsr_release_file(sr); return (status); }
/* * smb_ofile_close */ void smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec) { timestruc_t now; uint32_t flags = 0; SMB_OFILE_VALID(of); mutex_enter(&of->f_mutex); ASSERT(of->f_refcnt); switch (of->f_state) { case SMB_OFILE_STATE_OPEN: { of->f_state = SMB_OFILE_STATE_CLOSING; mutex_exit(&of->f_mutex); if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { smb_opipe_close(of); smb_server_dec_pipes(of->f_server); } else { smb_attr_t *pa = &of->f_pending_attr; /* * In here we make changes to of->f_pending_attr * while not holding of->f_mutex. This is OK * because we've changed f_state to CLOSING, * so no more threads will take this path. */ if (mtime_sec != 0) { pa->sa_vattr.va_mtime.tv_sec = mtime_sec; pa->sa_mask |= SMB_AT_MTIME; } /* * If we have ever modified data via this handle * (write or truncate) and if the mtime was not * set via this handle, update the mtime again * during the close. Windows expects this. * [ MS-FSA 2.1.5.4 "Update Timestamps" ] */ if (of->f_written && (pa->sa_mask & SMB_AT_MTIME) == 0) { pa->sa_mask |= SMB_AT_MTIME; gethrestime(&now); pa->sa_vattr.va_mtime = now; } if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) { if (smb_tree_has_feature(of->f_tree, SMB_TREE_CATIA)) { flags |= SMB_CATIA; } (void) smb_node_set_delete_on_close(of->f_node, of->f_cr, flags); } smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid); smb_node_destroy_lock_by_ofile(of->f_node, of); if (smb_node_is_file(of->f_node)) { (void) smb_fsop_close(of->f_node, of->f_mode, of->f_cr); smb_oplock_release(of->f_node, of); } if (smb_node_dec_open_ofiles(of->f_node) == 0) { /* * Last close. The f_pending_attr has * only times (atime,ctime,mtime) so * we can borrow it to commit the * n_pending_dosattr from the node. */ pa->sa_dosattr = of->f_node->n_pending_dosattr; if (pa->sa_dosattr != 0) pa->sa_mask |= SMB_AT_DOSATTR; /* Let's leave this zero when not in use. */ of->f_node->n_allocsz = 0; } if (pa->sa_mask != 0) { /* * Commit any pending attributes from * the ofile we're closing. Note that * we pass NULL as the ofile to setattr * so it will write to the file system * and not keep anything on the ofile. * This clears n_pending_dosattr if * there are no opens, otherwise the * dosattr will be pending again. */ (void) smb_node_setattr(NULL, of->f_node, of->f_cr, NULL, pa); } /* * Cancel any notify change requests that * may be using this open instance. */ if (of->f_node->n_fcn.fcn_count) smb_notify_file_closed(of); smb_server_dec_files(of->f_server); } atomic_dec_32(&of->f_tree->t_open_files); mutex_enter(&of->f_mutex); ASSERT(of->f_refcnt); ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); of->f_state = SMB_OFILE_STATE_CLOSED; break; } case SMB_OFILE_STATE_CLOSED: case SMB_OFILE_STATE_CLOSING: break; default: ASSERT(0); break; } mutex_exit(&of->f_mutex); }
int smb_pathname_reduce( smb_request_t *sr, cred_t *cred, const char *path, smb_node_t *share_root_node, smb_node_t *cur_node, smb_node_t **dir_node, char *last_component) { smb_node_t *root_node; pathname_t ppn; char *usepath; int lookup_flags = FOLLOW; int trailing_slash = 0; int err = 0; int len; smb_node_t *vss_cur_node; smb_node_t *vss_root_node; smb_node_t *local_cur_node; smb_node_t *local_root_node; ASSERT(dir_node); ASSERT(last_component); *dir_node = NULL; *last_component = '\0'; vss_cur_node = NULL; vss_root_node = NULL; if (sr && sr->tid_tree) { if (STYPE_ISIPC(sr->tid_tree->t_res_type)) return (EACCES); } if (SMB_TREE_IS_CASEINSENSITIVE(sr)) lookup_flags |= FIGNORECASE; if (path == NULL) return (EINVAL); if (*path == '\0') return (ENOENT); usepath = kmem_alloc(MAXPATHLEN, KM_SLEEP); if ((len = strlcpy(usepath, path, MAXPATHLEN)) >= MAXPATHLEN) { kmem_free(usepath, MAXPATHLEN); return (ENAMETOOLONG); } (void) strsubst(usepath, '\\', '/'); if (share_root_node) root_node = share_root_node; else root_node = sr->sr_server->si_root_smb_node; if (cur_node == NULL) cur_node = root_node; local_cur_node = cur_node; local_root_node = root_node; if (SMB_TREE_IS_DFSROOT(sr) && (sr->smb_flg2 & SMB_FLAGS2_DFS)) { err = smb_pathname_dfs_preprocess(sr, usepath, MAXPATHLEN); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); } if (sr && (sr->smb_flg2 & SMB_FLAGS2_REPARSE_PATH)) { err = smb_vss_lookup_nodes(sr, root_node, cur_node, usepath, &vss_cur_node, &vss_root_node); if (err != 0) { kmem_free(usepath, MAXPATHLEN); return (err); } len = strlen(usepath); local_cur_node = vss_cur_node; local_root_node = vss_root_node; } if (usepath[len - 1] == '/') trailing_slash = 1; (void) strcanon(usepath, "/"); (void) pn_alloc(&ppn); if ((err = pn_set(&ppn, usepath)) != 0) { (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); } /* * If a path does not have a trailing slash, strip off the * last component. (We only need to return an smb_node for * the second to last component; a name is returned for the * last component.) */ if (trailing_slash) { (void) strlcpy(last_component, ".", MAXNAMELEN); } else { (void) pn_setlast(&ppn); (void) strlcpy(last_component, ppn.pn_path, MAXNAMELEN); ppn.pn_path[0] = '\0'; } if ((strcmp(ppn.pn_buf, "/") == 0) || (ppn.pn_buf[0] == '\0')) { smb_node_ref(local_cur_node); *dir_node = local_cur_node; } else { err = smb_pathname(sr, ppn.pn_buf, lookup_flags, local_root_node, local_cur_node, NULL, dir_node, cred); } (void) pn_free(&ppn); kmem_free(usepath, MAXPATHLEN); /* * Prevent traversal to another file system if mount point * traversal is disabled. * * Note that we disregard whether the traversal of the path went * outside of the file system and then came back (say via a link). * This means that only symlinks that are expressed relatively to * the share root work. * * share_root_node is NULL when mapping a share, so we disregard * that case. */ if ((err == 0) && share_root_node) { if (share_root_node->vp->v_vfsp != (*dir_node)->vp->v_vfsp) { err = EACCES; if ((sr) && (sr)->tid_tree && smb_tree_has_feature((sr)->tid_tree, SMB_TREE_TRAVERSE_MOUNTS)) err = 0; } } if (err) { if (*dir_node) { (void) smb_node_release(*dir_node); *dir_node = NULL; } *last_component = 0; } if (vss_cur_node != NULL) (void) smb_node_release(vss_cur_node); if (vss_root_node != NULL) (void) smb_node_release(vss_root_node); return (err); }
/* * smb_common_rename * * Common code for renaming a file. * * If the source and destination are identical, we go through all * the checks but we don't actually do the rename. If the source * and destination files differ only in case, we do a case-sensitive * rename. Otherwise, we do a full case-insensitive rename. * * Returns NT status values. * * Similar to smb_make_link(), below. */ uint32_t smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) { smb_node_t *src_fnode, *src_dnode, *dst_dnode; smb_node_t *dst_fnode = 0; smb_node_t *tnode; char *new_name, *path; DWORD status; int rc, count; tnode = sr->tid_tree->t_snode; path = dst_fqi->fq_path.pn_path; /* Check if attempting to rename a stream - not yet supported */ rc = smb_rename_check_stream(src_fqi, dst_fqi); if (rc != 0) return (smb_rename_errno2status(rc)); /* * The source node may already have been provided, * i.e. when called by SMB1/SMB2 smb_setinfo_rename. * Not provided by smb_com_rename, smb_com_nt_rename. */ if (src_fqi->fq_fnode) { smb_node_start_crit(src_fqi->fq_fnode, RW_READER); smb_node_ref(src_fqi->fq_fnode); smb_node_ref(src_fqi->fq_dnode); } else { /* lookup and validate src node */ rc = smb_rename_lookup_src(sr); if (rc != 0) return (smb_rename_errno2status(rc)); } src_fnode = src_fqi->fq_fnode; src_dnode = src_fqi->fq_dnode; /* * Find the destination dnode and last component. * May already be provided, i.e. when called via * SMB1 trans2 setinfo. */ if (dst_fqi->fq_dnode) { /* called via smb_set_rename_info */ smb_node_ref(dst_fqi->fq_dnode); } else { /* called via smb2_setf_rename, smb_com_rename, etc. */ rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); if (rc != 0) { smb_rename_release_src(sr); return (smb_rename_errno2status(rc)); } } dst_dnode = dst_fqi->fq_dnode; new_name = dst_fqi->fq_last_comp; /* If exact name match in same directory, we're done */ if ((src_dnode == dst_dnode) && (strcmp(src_fnode->od_name, new_name) == 0)) { smb_rename_release_src(sr); smb_node_release(dst_dnode); return (0); } /* Lookup destination node */ rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, dst_dnode, new_name, &dst_fqi->fq_fnode); /* If the destination node doesn't already exist, validate new_name. */ if (rc == ENOENT) { if (smb_is_invalid_filename(new_name)) { smb_rename_release_src(sr); smb_node_release(dst_dnode); return (NT_STATUS_OBJECT_NAME_INVALID); } } /* * Handle case where changing case of the same directory entry. * * If we found the dst node in the same directory as the src node, * and their names differ only in case: * * If the tree is case sensitive (or mixed): * Do case sensitive lookup to see if exact match exists. * If the exact match is the same node as src_node we're done. * * If the tree is case insensitive: * There is currently no way to tell if the case is different * or not, so do the rename (unless the specified new name was * mangled). */ if ((rc == 0) && (src_dnode == dst_dnode) && (smb_strcasecmp(src_fnode->od_name, dst_fqi->fq_fnode->od_name, 0) == 0)) { smb_node_release(dst_fqi->fq_fnode); dst_fqi->fq_fnode = NULL; if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_NO_CASESENSITIVE)) { if (smb_strcasecmp(src_fnode->od_name, dst_fqi->fq_last_comp, 0) != 0) { smb_rename_release_src(sr); smb_node_release(dst_dnode); return (0); } } else { rc = smb_fsop_lookup(sr, sr->user_cr, SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name, &dst_fqi->fq_fnode); if ((rc == 0) && (dst_fqi->fq_fnode == src_fnode)) { smb_rename_release_src(sr); smb_node_release(dst_fqi->fq_fnode); smb_node_release(dst_dnode); return (0); } } } if ((rc != 0) && (rc != ENOENT)) { smb_rename_release_src(sr); smb_node_release(dst_fqi->fq_dnode); return (smb_rename_errno2status(rc)); } if (dst_fqi->fq_fnode) { /* * Destination already exists. Do delete checks. */ dst_fnode = dst_fqi->fq_fnode; if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) { smb_rename_release_src(sr); smb_node_release(dst_fnode); smb_node_release(dst_dnode); return (NT_STATUS_OBJECT_NAME_COLLISION); } (void) smb_oplock_break(sr, dst_fnode, SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH); /* * Wait (a little) for the oplock break to be * responded to by clients closing handles. * Hold node->n_lock as reader to keep new * ofiles from showing up after we check. */ smb_node_rdlock(dst_fnode); for (count = 0; count <= 12; count++) { status = smb_node_delete_check(dst_fnode); if (status != NT_STATUS_SHARING_VIOLATION) break; smb_node_unlock(dst_fnode); delay(MSEC_TO_TICK(100)); smb_node_rdlock(dst_fnode); } if (status != NT_STATUS_SUCCESS) { smb_node_unlock(dst_fnode); smb_rename_release_src(sr); smb_node_release(dst_fnode); smb_node_release(dst_dnode); return (NT_STATUS_ACCESS_DENIED); } /* * Note, the combination of these two: * smb_node_rdlock(node); * nbl_start_crit(node->vp, RW_READER); * is equivalent to this call: * smb_node_start_crit(node, RW_READER) * * Cleanup after this point should use: * smb_node_end_crit(dst_fnode) */ nbl_start_crit(dst_fnode->vp, RW_READER); /* * This checks nbl_share_conflict, nbl_lock_conflict */ status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE); if (status != NT_STATUS_SUCCESS) { smb_node_end_crit(dst_fnode); smb_rename_release_src(sr); smb_node_release(dst_fnode); smb_node_release(dst_dnode); return (NT_STATUS_ACCESS_DENIED); } new_name = dst_fnode->od_name; } rc = smb_fsop_rename(sr, sr->user_cr, src_dnode, src_fnode->od_name, dst_dnode, new_name); if (rc == 0) { /* * Note that renames in the same directory are normally * delivered in {old,new} pairs, and clients expect them * in that order, if both events are delivered. */ int a_src, a_dst; /* action codes */ if (src_dnode == dst_dnode) { a_src = FILE_ACTION_RENAMED_OLD_NAME; a_dst = FILE_ACTION_RENAMED_NEW_NAME; } else { a_src = FILE_ACTION_REMOVED; a_dst = FILE_ACTION_ADDED; } smb_node_notify_change(src_dnode, a_src, src_fnode->od_name); smb_node_notify_change(dst_dnode, a_dst, new_name); } smb_rename_release_src(sr); if (dst_fqi->fq_fnode) { smb_node_end_crit(dst_fnode); smb_node_release(dst_fnode); } smb_node_release(dst_dnode); return (smb_rename_errno2status(rc)); }
/* * smb_query_fileinfo * * Populate smb_queryinfo_t structure for SMB_FTYPE_DISK * (This should become an smb_ofile / smb_node function.) */ int smb_query_fileinfo(smb_request_t *sr, smb_node_t *node, uint16_t infolev, smb_queryinfo_t *qinfo) { int rc = 0; /* If shortname required but not supported -> OBJECT_NAME_NOT_FOUND */ if ((infolev == SMB_QUERY_FILE_ALT_NAME_INFO) || (infolev == SMB_FILE_ALT_NAME_INFORMATION)) { if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_SHORTNAMES)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); return (-1); } } (void) bzero(qinfo, sizeof (smb_queryinfo_t)); /* See: smb_query_encode_response */ qinfo->qi_attr.sa_mask = SMB_AT_ALL; rc = smb_node_getattr(sr, node, sr->user_cr, sr->fid_ofile, &qinfo->qi_attr); if (rc != 0) { smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (-1); } qinfo->qi_node = node; qinfo->qi_delete_on_close = (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) != 0; /* * The number of links reported should be the number of * non-deleted links. Thus if delete_on_close is set, * decrement the link count. */ if (qinfo->qi_delete_on_close && qinfo->qi_attr.sa_vattr.va_nlink > 0) { --(qinfo->qi_attr.sa_vattr.va_nlink); } /* * populate name, namelen and shortname ONLY for the information * levels that require these fields */ switch (infolev) { case SMB_QUERY_FILE_ALL_INFO: case SMB_FILE_ALL_INFORMATION: rc = smb_query_pathname(sr, node, B_TRUE, qinfo); break; case SMB_QUERY_FILE_NAME_INFO: case SMB_FILE_NAME_INFORMATION: rc = smb_query_pathname(sr, node, B_FALSE, qinfo); break; case SMB_QUERY_FILE_ALT_NAME_INFO: case SMB_FILE_ALT_NAME_INFORMATION: smb_query_shortname(node, qinfo); break; default: break; } if (rc != 0) { smbsr_errno(sr, rc); return (-1); } return (0); }
/* * 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); }
/* * smb_odir_wildcard_fileinfo * * odirent contains a directory entry, obtained from a vop_readdir. * If a case conflict is identified the filename is mangled and the * shortname is used as 'name', in place of odirent->od_name. * * If the looked up file is a link, we attempt to lookup the link target * to use its attributes in place of those of the files's. * If we fail to lookup the target of the link we use the original * file's attributes. * Check if the attributes match the search attributes. * * Although some file systems can have directories larger than * SMB_MAXDIRSIZE smb_odir_next_odirent ensures that no offset larger * than SMB_MAXDIRSIZE is returned. It is therefore safe to use the * offset as the cookie (uint32_t). * * Returns: 0 - success * ENOENT - no match, proceed to next entry * errno - error */ static int smb_odir_wildcard_fileinfo(smb_request_t *sr, smb_odir_t *od, smb_odirent_t *odirent, smb_fileinfo_t *fileinfo) { int rc; cred_t *cr; smb_node_t *fnode, *tgt_node; smb_attr_t attr; char *name; boolean_t case_conflict; ASSERT(sr); ASSERT(sr->sr_magic == SMB_REQ_MAGIC); ASSERT(od); ASSERT(od->d_magic == SMB_ODIR_MAGIC); ASSERT(MUTEX_HELD(&od->d_mutex)); bzero(fileinfo, sizeof (smb_fileinfo_t)); rc = smb_fsop_lookup(sr, od->d_cred, SMB_CASE_SENSITIVE, od->d_tree->t_snode, od->d_dnode, odirent->od_name, &fnode); if (rc != 0) return (rc); /* follow link to get target node & attr */ if (smb_node_is_symlink(fnode) && smb_odir_lookup_link(sr, od, odirent->od_name, &tgt_node)) { smb_node_release(fnode); fnode = tgt_node; } /* skip system files */ if (smb_node_is_system(fnode)) { smb_node_release(fnode); return (ENOENT); } /* * Windows directory listings return not only names, but * also some attributes. In Unix, you need some access to * get those attributes. Which credential should we use to * get those? If we're doing Access Based Enumeration (ABE) * we want this getattr to fail, which will cause the caller * to skip this entry. If we're NOT doing ABE, we normally * want to show all the directory entries (including their * attributes) so we want this getattr to succeed! */ if (smb_tree_has_feature(od->d_tree, SMB_TREE_ABE)) cr = od->d_cred; else cr = zone_kcred(); bzero(&attr, sizeof (attr)); attr.sa_mask = SMB_AT_ALL; rc = smb_node_getattr(NULL, fnode, cr, NULL, &attr); if (rc != 0) { smb_node_release(fnode); return (rc); } /* check search attributes */ if (!smb_sattr_check(attr.sa_dosattr, od->d_sattr)) { smb_node_release(fnode); return (ENOENT); } name = odirent->od_name; if (od->d_flags & SMB_ODIR_FLAG_SHORTNAMES) { case_conflict = ((od->d_flags & SMB_ODIR_FLAG_IGNORE_CASE) && (odirent->od_eflags & ED_CASE_CONFLICT)); if (case_conflict || smb_needs_mangled(name)) { smb_mangle(name, odirent->od_ino, fileinfo->fi_shortname, SMB_SHORTNAMELEN); } if (case_conflict) name = fileinfo->fi_shortname; } (void) strlcpy(fileinfo->fi_name, name, sizeof (fileinfo->fi_name)); fileinfo->fi_cookie = (uint32_t)od->d_offset; fileinfo->fi_dosattr = attr.sa_dosattr; fileinfo->fi_nodeid = attr.sa_vattr.va_nodeid; fileinfo->fi_size = attr.sa_vattr.va_size; fileinfo->fi_alloc_size = attr.sa_allocsz; fileinfo->fi_atime = attr.sa_vattr.va_atime; fileinfo->fi_mtime = attr.sa_vattr.va_mtime; fileinfo->fi_ctime = attr.sa_vattr.va_ctime; if (attr.sa_crtime.tv_sec) fileinfo->fi_crtime = attr.sa_crtime; else fileinfo->fi_crtime = attr.sa_vattr.va_mtime; smb_node_release(fnode); return (0); }