/* * 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); }
/* * FileFsVolumeInformation */ uint32_t smb2_qfs_volume(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; smb_node_t *snode; fsid_t fsid; uint32_t LabelLength; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); snode = tree->t_snode; fsid = SMB_NODE_FSID(snode); LabelLength = smb_wcequiv_strlen(tree->t_volume); /* * NT has the "supports objects" flag set to 1. */ (void) smb_mbc_encodef( &sr->raw_data, "qllb.U", 0LL, /* Volume creation time (q) */ fsid.val[0], /* serial no. (l) */ LabelLength, /* (l) */ 0, /* Supports objects (b) */ /* reserved (.) */ tree->t_volume); /* (U) */ return (0); }
smb_sdrc_t smb_com_create_directory(smb_request_t *sr) { int rc = 0; smb_pathname_t *pn = &sr->arg.dirop.fqi.fq_path; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } smb_pathname_init(sr, pn, pn->pn_path); if (!smb_pathname_validate(sr, pn) || !smb_validate_dirname(sr, pn)) { return (SDRC_ERROR); } if ((rc = smb_common_create_directory(sr)) != 0) { smbsr_errno(sr, rc); return (SDRC_ERROR); } rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
/* * Add the given share in the specified server. * If the share is a disk share, smb_vfs_hold() is * invoked to ensure that there is a hold on the * corresponding file system before the share is * added to shares AVL. * * If the share is an Autohome share and it is * already in the AVL only a reference count for * that share is incremented. */ static int smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr) { smb_avl_t *share_avl; smb_kshare_t *auto_shr; vnode_t *vp; int rc = 0; share_avl = &sv->sv_export.e_share_avl; if (!STYPE_ISDSK(shr->shr_type)) { if ((rc = smb_avl_add(share_avl, shr)) != 0) { cmn_err(CE_WARN, "export[%s]: failed caching (%d)", shr->shr_name, rc); } return (rc); } if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) { if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) { smb_avl_release(share_avl, auto_shr); return (EEXIST); } mutex_enter(&auto_shr->shr_mutex); auto_shr->shr_autocnt++; mutex_exit(&auto_shr->shr_mutex); smb_avl_release(share_avl, auto_shr); return (0); } if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) { cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)", shr->shr_name, shr->shr_path, rc); return (rc); } if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) { if ((rc = smb_avl_add(share_avl, shr)) != 0) { cmn_err(CE_WARN, "export[%s]: failed caching (%d)", shr->shr_name, rc); smb_vfs_rele(&sv->sv_export, vp->v_vfsp); } } else { cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)", shr->shr_name, shr->shr_path, rc); } VN_RELE(vp); return (rc); }
smb_sdrc_t smb_com_query_information_disk(smb_request_t *sr) { int rc; struct statvfs64 df; fsblkcnt64_t total_blocks, free_blocks; unsigned long block_size, unit_size; unsigned short blocks_per_unit, bytes_per_block; unsigned short total_units, free_units; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); return (SDRC_ERROR); } rc = smb_fsop_statfs(sr->user_cr, sr->tid_tree->t_snode, &df); if (rc != 0) { smbsr_errno(sr, rc); return (SDRC_ERROR); } unit_size = 1; block_size = df.f_frsize; total_blocks = df.f_blocks; free_blocks = df.f_bavail; /* * It seems that DOS clients cannot handle block sizes * bigger than 512 KB. So we have to set the block size at * most to 512 */ while (block_size > 512) { block_size >>= 1; unit_size <<= 1; } /* adjust blocks and sizes until they fit into a word */ while (total_blocks >= 0xFFFF) { total_blocks >>= 1; free_blocks >>= 1; if ((unit_size <<= 1) > 0xFFFF) { unit_size >>= 1; total_blocks = 0xFFFF; free_blocks <<= 1; break; } }
smb_sdrc_t smb_com_query_information(smb_request_t *sr) { char *path = sr->arg.dirop.fqi.fq_path.pn_path; uint16_t infolev = SMB_QUERY_INFORMATION; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_query_by_path(sr, NULL, infolev, path) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * Removes the share specified by 'shrname' from the AVL * tree of the given server if it's there. * * If the share is an Autohome share, the autohome count * is decremented and the share is only removed if the * count goes to zero. * * If the share is a disk share, the hold on the corresponding * file system is released before removing the share from * the AVL tree. */ static int smb_kshare_unexport(smb_server_t *sv, const char *shrname) { smb_avl_t *share_avl; smb_kshare_t key; smb_kshare_t *shr; vnode_t *vp; int rc; boolean_t auto_unexport; share_avl = &sv->sv_export.e_share_avl; key.shr_name = (char *)shrname; if ((shr = smb_avl_lookup(share_avl, &key)) == NULL) return (ENOENT); if ((shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) { mutex_enter(&shr->shr_mutex); shr->shr_autocnt--; auto_unexport = (shr->shr_autocnt == 0); mutex_exit(&shr->shr_mutex); if (!auto_unexport) { smb_avl_release(share_avl, shr); return (0); } } if (STYPE_ISDSK(shr->shr_type)) { if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) { smb_avl_release(share_avl, shr); cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode" " (%d)", shrname, rc); return (rc); } smb_vfs_rele(&sv->sv_export, vp->v_vfsp); VN_RELE(vp); } smb_avl_remove(share_avl, shr); smb_avl_release(share_avl, shr); return (0); }
/* * FileFsDeviceInformation */ uint32_t smb2_qfs_device(smb_request_t *sr) { smb_tree_t *tree = sr->tid_tree; uint32_t DeviceType; uint32_t Characteristics; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); DeviceType = FILE_DEVICE_DISK; Characteristics = FILE_DEVICE_IS_MOUNTED; (void) smb_mbc_encodef( &sr->raw_data, "ll", DeviceType, Characteristics); return (0); }
/* * smb_com_trans2_query_path_information */ smb_sdrc_t smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa) { uint16_t infolev; char *path; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u", sr, &infolev, &path) != 0) return (SDRC_ERROR); if (smb_query_by_path(sr, xa, infolev, path) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * FileFsSizeInformation */ uint32_t smb2_qfs_size(smb_request_t *sr) { smb_fssize_t fssize; smb_tree_t *tree = sr->tid_tree; int rc; if (!STYPE_ISDSK(tree->t_res_type)) return (NT_STATUS_INVALID_PARAMETER); rc = smb_fssize(sr, &fssize); if (rc) return (smb_errno2status(rc)); (void) smb_mbc_encodef( &sr->raw_data, "qqll", fssize.fs_caller_units, fssize.fs_caller_avail, fssize.fs_sectors_per_unit, fssize.fs_bytes_per_sector); return (0); }
/* * smb_com_trans2_find_next2 * * Client Request Value * ================================== ================================= * * WordCount 15 * SetupCount 1 * Setup[0] TRANS2_FIND_NEXT2 * * Parameter Block Encoding Description * ================================== ================================= * * USHORT Sid; Search handle * USHORT SearchCount; Maximum number of entries to * return * USHORT InformationLevel; Levels described in * TRANS2_FIND_FIRST2 request * ULONG ResumeKey; Value returned by previous find2 * call * USHORT Flags; Additional information: bit set- * 0 - close search after this * request * 1 - close search if end of search * reached * 2 - return resume keys for each * entry found * 3 - resume/continue from previous * ending place * 4 - find with backup intent * STRING FileName; Resume file name * * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2 * call. If Bit3 of Flags is set, then FileName may be the NULL string, * since the search is continued from the previous TRANS2_FIND request. * Otherwise, FileName must not be more than 256 characters long. * * Response Field Description * ================================== ================================= * * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of * last entry, if server needs it to * resume search; else 0 * UCHAR Data[TotalDataCount] Level dependent info about the * matches found in the search * * * The last parameter in the request is a filename, which is a * null-terminated unicode string. * * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr, * &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname) * * The filename parameter is not currently decoded because we * expect a 2-byte null but Mac OS 10 clients send a 1-byte null, * which leads to a decode error. * Thus, we do not support resume by filename. We treat a request * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST. */ smb_sdrc_t smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t odid; smb_odir_t *od; smb_find_args_t args; smb_odir_resume_t odir_resume; bzero(&args, sizeof (args)); bzero(&odir_resume, sizeof (odir_resume)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr, &odid, &args.fa_maxcount, &args.fa_infolev, &odir_resume.or_cookie, &args.fa_fflag, &odir_resume.or_fname) != 0) { return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERROR_INVALID_HANDLE); return (SDRC_ERROR); } /* * Set the correct position in the directory. * * "Continue from last" is easy, but due to a history of * buggy server implementations, most clients don't use * that method. The most widely used (and reliable) is * resume by file name. Unfortunately, that can't really * be fully supported unless your file system stores all * directory entries in some sorted order (like NTFS). * We can partially support resume by name, where the only * name we're ever asked to resume on is the same as the * most recent we returned. That's always what the client * gives us as the resume name, so we can simply remember * the last name/offset pair and use that to position on * the following FindNext call. In the unlikely event * that the client asks to resume somewhere else, we'll * use the numeric resume key, and hope the client gives * correctly uses one of the resume keys we provided. */ if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) { odir_resume.or_type = SMB_ODIR_RESUME_CONT; } else { odir_resume.or_type = SMB_ODIR_RESUME_FNAME; } smb_odir_resume_at(od, &odir_resume); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ return (SDRC_SUCCESS); }
/* * smb_com_trans2_find_first2 * * Client Request Value * ============================ ================================== * * UCHAR WordCount 15 * UCHAR TotalDataCount Total size of extended attribute list * UCHAR SetupCount 1 * UCHAR Setup[0] TRANS2_FIND_FIRST2 * * Parameter Block Encoding Description * ============================ ================================== * USHORT SearchAttributes; * USHORT SearchCount; Maximum number of entries to return * USHORT Flags; Additional information: * Bit 0 - close search after this request * Bit 1 - close search if end of search * reached * Bit 2 - return resume keys for each * entry found * Bit 3 - continue search from previous * ending place * Bit 4 - find with backup intent * USHORT InformationLevel; See below * ULONG SearchStorageType; * STRING FileName; Pattern for the search * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is * QUERY_EAS_FROM_LIST * * Response Parameter Block Description * ============================ ================================== * * USHORT Sid; Search handle * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of last * entry, if server needs it to resume * search; else 0 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches * found in the search */ smb_sdrc_t smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t sattr, odid; smb_pathname_t *pn; smb_odir_t *od; smb_find_args_t args; uint32_t odir_flags = 0; bzero(&args, sizeof (smb_find_args_t)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } pn = &sr->arg.dirop.fqi.fq_path; if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr, &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &pn->pn_path) != 0) { return (SDRC_ERROR); } smb_pathname_init(sr, pn, pn->pn_path); if (!smb_pathname_validate(sr, pn)) return (-1); if (smb_is_stream_name(pn->pn_path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) { sr->user_cr = smb_user_getprivcred(sr->uid_user); odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT; } args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); odid = smb_odir_open(sr, pn->pn_path, sattr, odir_flags); if (odid == 0) { if (sr->smb_error.status == NT_STATUS_OBJECT_PATH_NOT_FOUND) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); } return (SDRC_ERROR); } od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) return (SDRC_ERROR); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if (count == 0) { smb_odir_close(od); smb_odir_release(od); smbsr_errno(sr, ENOENT); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww", odid, /* Search ID */ count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa) { struct open_param *op = &sr->arg.open; uint32_t creation_time; uint32_t alloc_size; uint16_t flags; uint16_t file_attr; int rc; bzero(op, sizeof (sr->arg.open)); rc = smb_mbc_decodef(&xa->req_param_mb, "%wwwwlwl10.u", sr, &flags, &op->omode, &op->fqi.fq_sattr, &file_attr, &creation_time, &op->ofun, &alloc_size, &op->fqi.fq_path.pn_path); if (rc != 0) return (SDRC_ERROR); if ((creation_time != 0) && (creation_time != UINT_MAX)) op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->dattr = file_attr; op->dsize = alloc_size; op->create_options = FILE_NON_DIRECTORY_FILE; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); op->create_disposition = smb_ofun_to_crdisposition(op->ofun); if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) op->create_disposition = FILE_CREATE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (sr->smb_flg & SMB_FLAGS_OPLOCK) { if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) op->op_oplock_level = SMB_OPLOCK_BATCH; else op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; } else { op->op_oplock_level = SMB_OPLOCK_NONE; } if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); if (op->op_oplock_level != SMB_OPLOCK_NONE) op->action_taken |= SMB_OACT_LOCK; else op->action_taken &= ~SMB_OACT_LOCK; file_attr = op->dattr & FILE_ATTRIBUTE_MASK; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) op->dsize = 0; (void) smb_mbc_encodef(&xa->rep_param_mb, "wwllwwwwlwl", sr->smb_fid, file_attr, (uint32_t)0, /* creation time */ (uint32_t)op->dsize, op->omode, op->ftype, op->devstate, op->action_taken, op->fileid, (uint16_t)0, /* EA error offset */ (uint32_t)0); /* EA list length */ return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_open_andx(smb_request_t *sr) { struct open_param *op = &sr->arg.open; uint16_t file_attr; smb_attr_t attr; int rc; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) { smbsr_error(sr, 0, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } op->create_options = FILE_NON_DIRECTORY_FILE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); if (smb_open_dsize_check && op->dsize > UINT_MAX) { smbsr_error(sr, 0, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->op_oplock_level != SMB_OPLOCK_NONE) op->action_taken |= SMB_OACT_LOCK; else op->action_taken &= ~SMB_OACT_LOCK; file_attr = op->dattr & FILE_ATTRIBUTE_MASK; if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { smb_node_t *node = sr->fid_ofile->f_node; if (smb_node_getattr(sr, node, &attr) != 0) { smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (SDRC_ERROR); } rc = smbsr_encode_result(sr, 15, 0, "bb.wwwllwwwwl2.w", 15, sr->andx_com, VAR_BCC, sr->smb_fid, file_attr, smb_time_gmt_to_local(sr, attr.sa_vattr.va_mtime.tv_sec), (uint32_t)op->dsize, op->omode, op->ftype, op->devstate, op->action_taken, op->fileid, 0); } else { rc = smbsr_encode_result(sr, 15, 0, "bb.wwwllwwwwl2.w", 15, sr->andx_com, VAR_BCC, sr->smb_fid, file_attr, 0L, 0L, op->omode, op->ftype, op->devstate, op->action_taken, op->fileid, 0); } return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
smb_sdrc_t smb_com_delete_directory(smb_request_t *sr) { int rc; uint32_t flags = 0; smb_fqi_t *fqi; smb_node_t *tnode; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } fqi = &sr->arg.dirop.fqi; tnode = sr->tid_tree->t_snode; smb_pathname_init(sr, &fqi->fq_path, fqi->fq_path.pn_path); if (!smb_pathname_validate(sr, &fqi->fq_path) || !smb_validate_dirname(sr, &fqi->fq_path)) { return (SDRC_ERROR); } rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path, tnode, tnode, &fqi->fq_dnode, fqi->fq_last_comp); if (rc != 0) { smbsr_errno(sr, rc); return (SDRC_ERROR); } rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode); if (rc != 0) { if (rc == ENOENT) smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); else smbsr_errno(sr, rc); smb_node_release(fqi->fq_dnode); return (SDRC_ERROR); } /* * Delete should fail if this is the root of a share * or a DFS link */ if ((fqi->fq_fnode == tnode) || smb_node_is_dfslink(fqi->fq_fnode)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); smb_node_release(fqi->fq_dnode); smb_node_release(fqi->fq_fnode); return (SDRC_ERROR); } if (!smb_node_is_dir(fqi->fq_fnode)) { smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, ERRDOS, ERROR_PATH_NOT_FOUND); smb_node_release(fqi->fq_dnode); smb_node_release(fqi->fq_fnode); return (SDRC_ERROR); } /* * Using kcred because we just want the DOS attrs * and don't want access errors for this. */ fqi->fq_fattr.sa_mask = SMB_AT_DOSATTR; rc = smb_node_getattr(sr, fqi->fq_fnode, zone_kcred(), NULL, &fqi->fq_fattr); if (rc != 0) { smbsr_errno(sr, rc); smb_node_release(fqi->fq_dnode); smb_node_release(fqi->fq_fnode); return (SDRC_ERROR); } if ((fqi->fq_fattr.sa_dosattr & FILE_ATTRIBUTE_READONLY) || (smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, DELETE) != NT_STATUS_SUCCESS)) { smbsr_error(sr, NT_STATUS_CANNOT_DELETE, ERRDOS, ERROR_ACCESS_DENIED); smb_node_release(fqi->fq_dnode); smb_node_release(fqi->fq_fnode); return (SDRC_ERROR); } if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; rc = smb_fsop_rmdir(sr, sr->user_cr, fqi->fq_dnode, fqi->fq_fnode->od_name, flags); smb_node_release(fqi->fq_fnode); smb_node_release(fqi->fq_dnode); if (rc != 0) { if (rc == EEXIST) smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY, ERRDOS, ERROR_DIR_NOT_EMPTY); else smbsr_errno(sr, rc); return (SDRC_ERROR); } rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
/* * smb_com_trans2_find_first2 * * Client Request Value * ============================ ================================== * * UCHAR WordCount 15 * UCHAR TotalDataCount Total size of extended attribute list * UCHAR SetupCount 1 * UCHAR Setup[0] TRANS2_FIND_FIRST2 * * Parameter Block Encoding Description * ============================ ================================== * USHORT SearchAttributes; * USHORT SearchCount; Maximum number of entries to return * USHORT Flags; Additional information: * Bit 0 - close search after this request * Bit 1 - close search if end of search * reached * Bit 2 - return resume keys for each * entry found * Bit 3 - continue search from previous * ending place * Bit 4 - find with backup intent * USHORT InformationLevel; See below * ULONG SearchStorageType; * STRING FileName; Pattern for the search * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is * QUERY_EAS_FROM_LIST * * Response Parameter Block Description * ============================ ================================== * * USHORT Sid; Search handle * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of last * entry, if server needs it to resume * search; else 0 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches * found in the search */ smb_sdrc_t smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t sattr, odid; char *path; smb_odir_t *od; smb_find_args_t args; boolean_t eos; uint32_t odir_flags = 0; bzero(&args, sizeof (smb_find_args_t)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr, &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &path) != 0) { return (SDRC_ERROR); } if (smb_is_stream_name(path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) { sr->user_cr = smb_user_getprivcred(sr->uid_user); odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT; } args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) (void) smb_convert_wildcards(path); odid = smb_odir_open(sr, path, sattr, odir_flags); if (odid == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) return (SDRC_ERROR); count = smb_trans2_find_entries(sr, xa, od, &args, &eos); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if (count == 0) { smb_odir_close(od); smb_odir_release(od); smbsr_errno(sr, ENOENT); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww", odid, count, (eos) ? 1 : 0, 0, 0); return (SDRC_SUCCESS); }
/* * Allocate a tree. */ static smb_tree_t * smb_tree_alloc( smb_user_t *user, const smb_share_t *si, int32_t stype, smb_node_t *snode, uint32_t access) { smb_tree_t *tree; uint16_t tid; if (smb_idpool_alloc(&user->u_tid_pool, &tid)) return (NULL); tree = kmem_cache_alloc(user->u_server->si_cache_tree, KM_SLEEP); bzero(tree, sizeof (smb_tree_t)); if (STYPE_ISDSK(stype)) { if (smb_tree_getattr(si, snode, tree) != 0) { smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } } if (smb_idpool_constructor(&tree->t_fid_pool)) { smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } if (smb_idpool_constructor(&tree->t_odid_pool)) { smb_idpool_destructor(&tree->t_fid_pool); smb_idpool_free(&user->u_tid_pool, tid); kmem_cache_free(user->u_server->si_cache_tree, tree); return (NULL); } smb_llist_constructor(&tree->t_ofile_list, sizeof (smb_ofile_t), offsetof(smb_ofile_t, f_lnd)); smb_llist_constructor(&tree->t_odir_list, sizeof (smb_odir_t), offsetof(smb_odir_t, d_lnd)); (void) strlcpy(tree->t_sharename, si->shr_name, sizeof (tree->t_sharename)); (void) strlcpy(tree->t_resource, si->shr_path, sizeof (tree->t_resource)); mutex_init(&tree->t_mutex, NULL, MUTEX_DEFAULT, NULL); tree->t_user = user; tree->t_session = user->u_session; tree->t_server = user->u_server; tree->t_refcnt = 1; tree->t_tid = tid; tree->t_res_type = stype; tree->t_state = SMB_TREE_STATE_CONNECTED; tree->t_magic = SMB_TREE_MAGIC; tree->t_access = access; tree->t_connect_time = gethrestime_sec(); /* if FS is readonly, enforce that here */ if (tree->t_flags & SMB_TREE_READONLY) tree->t_access &= ~ACE_ALL_WRITE_PERMS; if (STYPE_ISDSK(stype)) { smb_node_ref(snode); tree->t_snode = snode; tree->t_acltype = smb_fsop_acltype(snode); } smb_llist_enter(&user->u_tree_list, RW_WRITER); smb_llist_insert_head(&user->u_tree_list, tree); smb_llist_exit(&user->u_tree_list); atomic_inc_32(&user->u_session->s_tree_cnt); atomic_inc_32(&user->u_server->sv_open_trees); return (tree); }
smb_sdrc_t smb_com_nt_create_andx(struct smb_request *sr) { struct open_param *op = &sr->arg.open; unsigned char OplockLevel; unsigned char DirFlag; smb_attr_t attr; smb_node_t *node; int rc; if ((op->create_options & FILE_DELETE_ON_CLOSE) && !(op->desired_access & DELETE)) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->dattr & FILE_FLAG_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE) op->create_options |= FILE_DELETE_ON_CLOSE; if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS) op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT; if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); if (op->rootdirfid == 0) { op->fqi.fq_dnode = sr->tid_tree->t_snode; } else { op->dir = smb_ofile_lookup_by_fid(sr->tid_tree, (uint16_t)op->rootdirfid); if (op->dir == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } op->fqi.fq_dnode = op->dir->f_node; } if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); if (STYPE_ISDSK(sr->tid_tree->t_res_type)) { switch (op->op_oplock_level) { case SMB_OPLOCK_EXCLUSIVE: OplockLevel = 1; break; case SMB_OPLOCK_BATCH: OplockLevel = 2; break; case SMB_OPLOCK_LEVEL_II: OplockLevel = 3; break; case SMB_OPLOCK_NONE: default: OplockLevel = 0; break; } if (op->create_options & FILE_DELETE_ON_CLOSE) smb_ofile_set_delete_on_close(sr->fid_ofile); node = sr->fid_ofile->f_node; DirFlag = smb_node_is_dir(node) ? 1 : 0; if (smb_node_getattr(sr, node, &attr) != 0) { smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (SDRC_ERROR); } rc = smbsr_encode_result(sr, 34, 0, "bb.wbwlTTTTlqqwwbw", 34, sr->andx_com, 0x67, OplockLevel, sr->smb_fid, op->action_taken, &attr.sa_crtime, &attr.sa_vattr.va_atime, &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr & FILE_ATTRIBUTE_MASK, attr.sa_allocsz, attr.sa_vattr.va_size, op->ftype, op->devstate, DirFlag, 0); } else { /* Named PIPE */ OplockLevel = 0; rc = smbsr_encode_result(sr, 34, 0, "bb.wbwlqqqqlqqwwbw", 34, sr->andx_com, 0x67, OplockLevel, sr->smb_fid, op->action_taken, 0LL, 0LL, 0LL, 0LL, FILE_ATTRIBUTE_NORMAL, 0x1000LL, 0LL, op->ftype, op->devstate, 0, 0); } return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }