diff options
Diffstat (limited to 'usr/src/uts/common/fs')
34 files changed, 2255 insertions, 3009 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c index 8525dcb4b7..74049d0c60 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c +++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c @@ -441,14 +441,11 @@ smb_open_subr(smb_request_t *sr) if (rc == 0) { last_comp_found = B_TRUE; - (void) strcpy(op->fqi.fq_od_name, - op->fqi.fq_fnode->od_name); rc = smb_node_getattr(sr, op->fqi.fq_fnode, &op->fqi.fq_fattr); if (rc != 0) { smb_node_release(op->fqi.fq_fnode); smb_node_release(op->fqi.fq_dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (sr->smb_error.status); @@ -459,7 +456,6 @@ smb_open_subr(smb_request_t *sr) rc = 0; } else { smb_node_release(op->fqi.fq_dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_errno(sr, rc); return (sr->smb_error.status); } @@ -481,7 +477,6 @@ smb_open_subr(smb_request_t *sr) smb_node_release(op->fqi.fq_fnode); smb_node_release(op->fqi.fq_dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); return (NT_STATUS_ACCESS_DENIED); @@ -501,7 +496,6 @@ smb_open_subr(smb_request_t *sr) if (op->create_options & FILE_NON_DIRECTORY_FILE) { smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY, ERRDOS, ERROR_ACCESS_DENIED); return (NT_STATUS_FILE_IS_A_DIRECTORY); @@ -511,7 +505,6 @@ smb_open_subr(smb_request_t *sr) (op->nt_flags & NT_CREATE_FLAG_OPEN_TARGET_DIR)) { smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, ERRDOS, ERROR_DIRECTORY); return (NT_STATUS_NOT_A_DIRECTORY); @@ -525,7 +518,6 @@ smb_open_subr(smb_request_t *sr) if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_DELETE_PENDING, ERRDOS, ERROR_ACCESS_DENIED); return (NT_STATUS_DELETE_PENDING); @@ -537,7 +529,6 @@ smb_open_subr(smb_request_t *sr) if (op->create_disposition == FILE_CREATE) { smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, ERRDOS, ERROR_FILE_EXISTS); return (NT_STATUS_OBJECT_NAME_COLLISION); @@ -557,7 +548,6 @@ smb_open_subr(smb_request_t *sr) FILE_APPEND_DATA)) { smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); return (NT_STATUS_ACCESS_DENIED); @@ -582,7 +572,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); return (NT_STATUS_ACCESS_DENIED); @@ -596,7 +585,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); return (status); } @@ -609,7 +597,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); if (status == NT_STATUS_PRIVILEGE_NOT_HELD) { smbsr_error(sr, status, @@ -631,7 +618,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (NT_STATUS_ACCESS_DENIED); @@ -654,7 +640,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_errno(sr, rc); return (sr->smb_error.status); } @@ -671,7 +656,6 @@ smb_open_subr(smb_request_t *sr) smb_node_unlock(node); smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); return (sr->smb_error.status); } } @@ -696,7 +680,6 @@ smb_open_subr(smb_request_t *sr) if ((op->create_disposition == FILE_OPEN) || (op->create_disposition == FILE_OVERWRITE)) { smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); return (NT_STATUS_OBJECT_NAME_NOT_FOUND); @@ -705,7 +688,6 @@ smb_open_subr(smb_request_t *sr) if ((is_dir == 0) && (!is_stream) && smb_is_invalid_filename(op->fqi.fq_last_comp)) { smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (NT_STATUS_OBJECT_NAME_INVALID); @@ -752,7 +734,6 @@ smb_open_subr(smb_request_t *sr) if (rc != 0) { smb_node_unlock(dnode); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_errno(sr, rc); return (sr->smb_error.status); } @@ -769,7 +750,6 @@ smb_open_subr(smb_request_t *sr) smb_node_release(node); smb_node_unlock(dnode); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); return (status); } } else { @@ -785,7 +765,6 @@ smb_open_subr(smb_request_t *sr) if (rc != 0) { smb_node_unlock(dnode); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); smbsr_errno(sr, rc); return (sr->smb_error.status); } @@ -796,7 +775,6 @@ smb_open_subr(smb_request_t *sr) created = B_TRUE; op->action_taken = SMB_OACT_CREATED; - node->flags |= NODE_FLAGS_CREATED; } if (max_requested) { @@ -857,7 +835,6 @@ smb_open_subr(smb_request_t *sr) if (created) smb_node_unlock(dnode); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); return (status); } @@ -896,7 +873,6 @@ smb_open_subr(smb_request_t *sr) smb_node_release(node); smb_node_release(dnode); - SMB_NULL_FQI_NODES(op->fqi); return (NT_STATUS_SUCCESS); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_create.c b/usr/src/uts/common/fs/smbsrv/smb_create.c index fe01c89490..ddc8408248 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_create.c +++ b/usr/src/uts/common/fs/smbsrv/smb_create.c @@ -185,7 +185,7 @@ smb_common_create(smb_request_t *sr) uint32_t status; if ((op->mtime.tv_sec != 0) && (op->mtime.tv_sec != UINT_MAX)) - op->mtime.tv_sec = smb_local2gmt(sr, op->mtime.tv_sec); + op->mtime.tv_sec = smb_time_local_to_gmt(sr, op->mtime.tv_sec); op->mtime.tv_nsec = 0; op->dsize = 0; op->omode = SMB_DA_ACCESS_READ_WRITE | SMB_DA_SHARE_COMPATIBILITY; diff --git a/usr/src/uts/common/fs/smbsrv/smb_delete.c b/usr/src/uts/common/fs/smbsrv/smb_delete.c index c486ae9f30..7c1481bfae 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_delete.c +++ b/usr/src/uts/common/fs/smbsrv/smb_delete.c @@ -31,7 +31,7 @@ static int smb_delete_check_path(smb_request_t *, boolean_t *); static int smb_delete_single_file(smb_request_t *, smb_error_t *); static int smb_delete_multiple_files(smb_request_t *, smb_error_t *); -static int smb_delete_find_fname(smb_request_t *, smb_odir_t *); +static int smb_delete_find_fname(smb_request_t *, smb_odir_t *, char *, int); static int smb_delete_check_dosattr(smb_request_t *, smb_error_t *); static int smb_delete_remove_file(smb_request_t *, smb_error_t *); @@ -276,6 +276,7 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err) smb_fqi_t *fqi; uint16_t odid; smb_odir_t *od; + char namebuf[MAXNAMELEN]; fqi = &sr->arg.dirop.fqi; @@ -292,13 +293,13 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err) return (-1); for (;;) { - rc = smb_delete_find_fname(sr, od); + rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN); if (rc != 0) break; rc = smb_fsop_lookup_name(sr, sr->user_cr, 0, sr->tid_tree->t_snode, fqi->fq_dnode, - fqi->fq_od_name, &fqi->fq_fnode); + namebuf, &fqi->fq_fnode); if (rc != 0) break; @@ -351,8 +352,8 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err) /* * smb_delete_find_fname * - * Find next filename that matches search pattern (fqi->fq_last_comp) - * and save it in fqi->fq_od_name. + * Find next filename that matches search pattern and return it + * in namebuf. * * Case insensitivity note: * If the tree is case insensitive and there's a case conflict @@ -365,7 +366,7 @@ smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err) * errno */ static int -smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od) +smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len) { int rc; smb_odirent_t *odirent; @@ -373,9 +374,7 @@ smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od) char *name; char shortname[SMB_SHORTNAMELEN]; char name83[SMB_SHORTNAMELEN]; - smb_fqi_t *fqi; - fqi = &sr->arg.dirop.fqi; odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP); rc = smb_odir_read(sr, od, odirent, &eos); @@ -397,7 +396,7 @@ smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od) } else { name = odirent->od_name; } - (void) strlcpy(fqi->fq_od_name, name, sizeof (fqi->fq_od_name)); + (void) strlcpy(namebuf, name, len); kmem_free(odirent, sizeof (smb_odirent_t)); return (0); diff --git a/usr/src/uts/common/fs/smbsrv/smb_directory.c b/usr/src/uts/common/fs/smbsrv/smb_directory.c index c59d8cce2e..60d95d0198 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_directory.c +++ b/usr/src/uts/common/fs/smbsrv/smb_directory.c @@ -91,9 +91,9 @@ smb_sdrc_t smb_com_create_directory(smb_request_t *sr) { smb_dirpath_t *spp; - smb_attr_t *attr; DWORD status; int rc = 0; + char *path = sr->arg.dirop.fqi.fq_path.pn_path; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, @@ -101,13 +101,13 @@ smb_com_create_directory(smb_request_t *sr) return (SDRC_ERROR); } - if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.fq_path.pn_path)) { + if (!smb_dirpath_isvalid(path)) { smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, ERRDOS, ERROR_BAD_PATHNAME); return (SDRC_ERROR); } - status = smb_validate_dirname(sr->arg.dirop.fqi.fq_path.pn_path); + status = smb_validate_dirname(path); if (status != 0) { smbsr_error(sr, status, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); @@ -126,20 +126,17 @@ smb_com_create_directory(smb_request_t *sr) case 0: break; case EEXIST: - attr = &sr->arg.dirop.fqi.fq_fattr; - - if (attr->sa_vattr.va_type != VDIR) { - smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, - ERRDOS, ERROR_PATH_NOT_FOUND); - return (SDRC_ERROR); - } break; case ENOENT: smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); return (SDRC_ERROR); case ENOTDIR: - smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, + /* + * Spec says status should be OBJECT_PATH_INVALID + * but testing shows OBJECT_PATH_NOT_FOUND + */ + smbsr_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND, ERRDOS, ERROR_PATH_NOT_FOUND); return (SDRC_ERROR); default: @@ -149,7 +146,11 @@ smb_com_create_directory(smb_request_t *sr) } if (rc != 0) { - smbsr_errno(sr, rc); + if (rc == EEXIST) + smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, + ERRDOS, ERROR_FILE_EXISTS); + else + smbsr_errno(sr, rc); return (SDRC_ERROR); } @@ -195,25 +196,36 @@ smb_common_create_directory(smb_request_t *sr) { int rc; smb_attr_t new_attr; - smb_node_t *dnode; - smb_node_t *node; + smb_fqi_t *fqi; + smb_node_t *tnode; - sr->arg.dirop.fqi.fq_sattr = 0; + fqi = &sr->arg.dirop.fqi; + tnode = sr->tid_tree->t_snode; - rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_NOT_EXIST); - if (rc) + 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) return (rc); - /* - * Because of FQM_PATH_MUST_NOT_EXIST and the successful return - * value, only fqi.fq_dnode has a valid parameter (fqi.fq_fnode - * is NULL). - */ - dnode = sr->arg.dirop.fqi.fq_dnode; + /* lookup node - to ensure that it does NOT exist */ + 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) { + smb_node_release(fqi->fq_dnode); + smb_node_release(fqi->fq_fnode); + return (EEXIST); + } + if (rc != ENOENT) { + smb_node_release(fqi->fq_dnode); + return (rc); + } - rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_ADD_SUBDIRECTORY); - if (rc != NT_STATUS_SUCCESS) + rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode, + FILE_ADD_SUBDIRECTORY); + if (rc != NT_STATUS_SUCCESS) { + smb_node_release(fqi->fq_dnode); return (EACCES); + } /* * Explicitly set sa_dosattr, otherwise the file system may @@ -226,28 +238,17 @@ smb_common_create_directory(smb_request_t *sr) new_attr.sa_vattr.va_mode = 0777; new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_DOSATTR; - if ((rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, - sr->arg.dirop.fqi.fq_last_comp, &new_attr, - &sr->arg.dirop.fqi.fq_fnode)) != 0) { - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); - return (rc); - } - - node = sr->arg.dirop.fqi.fq_fnode; - rc = smb_node_getattr(sr, node, &sr->arg.dirop.fqi.fq_fattr); + rc = smb_fsop_mkdir(sr, sr->user_cr, fqi->fq_dnode, fqi->fq_last_comp, + &new_attr, &fqi->fq_fnode); if (rc != 0) { - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + smb_node_release(fqi->fq_dnode); return (rc); } - node->flags |= NODE_FLAGS_CREATED; sr->arg.open.create_options = FILE_DIRECTORY_FILE; - smb_node_release(node); - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + smb_node_release(fqi->fq_dnode); + smb_node_release(fqi->fq_fnode); return (0); } @@ -364,10 +365,10 @@ smb_post_delete_directory(smb_request_t *sr) smb_sdrc_t smb_com_delete_directory(smb_request_t *sr) { - smb_node_t *dnode; - smb_attr_t *attr; 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, @@ -375,50 +376,64 @@ smb_com_delete_directory(smb_request_t *sr) return (SDRC_ERROR); } - sr->arg.dirop.fqi.fq_sattr = 0; + fqi = &sr->arg.dirop.fqi; + tnode = sr->tid_tree->t_snode; - rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); - if (rc) { + 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); } - attr = &sr->arg.dirop.fqi.fq_fattr; - if (attr->sa_vattr.va_type != VDIR) { + rc = smb_node_getattr(sr, fqi->fq_fnode, &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_vattr.va_type != VDIR) { 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); } - dnode = sr->arg.dirop.fqi.fq_fnode; - rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE); - - if ((rc != NT_STATUS_SUCCESS) || - attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) { - smb_node_release(dnode); - smb_node_release(sr->arg.dirop.fqi.fq_dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + 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); } - smb_node_release(dnode); - - dnode = sr->arg.dirop.fqi.fq_dnode; - if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; - rc = smb_fsop_rmdir(sr, sr->user_cr, dnode, - sr->arg.dirop.fqi.fq_od_name, flags); + 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) { - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); if (rc == EEXIST) smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY, ERRDOS, ERROR_DIR_NOT_EMPTY); @@ -427,9 +442,6 @@ smb_com_delete_directory(smb_request_t *sr) return (SDRC_ERROR); } - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); - rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } @@ -483,8 +495,10 @@ smb_post_check_directory(smb_request_t *sr) smb_sdrc_t smb_com_check_directory(smb_request_t *sr) { - smb_node_t *dnode; int rc; + smb_fqi_t *fqi; + smb_node_t *tnode; + char *path; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, @@ -492,21 +506,33 @@ smb_com_check_directory(smb_request_t *sr) return (SDRC_ERROR); } - if (sr->arg.dirop.fqi.fq_path.pn_path[0] == '\0') { + fqi = &sr->arg.dirop.fqi; + path = fqi->fq_path.pn_path; + + if (path[0] == '\0') { rc = smbsr_encode_empty_result(sr); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } - if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.fq_path.pn_path)) { + if (!smb_dirpath_isvalid(path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_PATH_NOT_FOUND); return (SDRC_ERROR); } - sr->arg.dirop.fqi.fq_sattr = 0; + tnode = sr->tid_tree->t_snode; - rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); - if (rc) { + rc = smb_pathname_reduce(sr, sr->user_cr, 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); + smb_node_release(fqi->fq_dnode); + if (rc != 0) { if (rc == ENOENT) smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_PATH_NOT_FOUND); @@ -515,22 +541,23 @@ smb_com_check_directory(smb_request_t *sr) return (SDRC_ERROR); } - smb_node_release(sr->arg.dirop.fqi.fq_dnode); - - dnode = sr->arg.dirop.fqi.fq_fnode; + rc = smb_node_getattr(sr, fqi->fq_fnode, &fqi->fq_fattr); + if (rc != 0) { + smbsr_errno(sr, rc); + smb_node_release(fqi->fq_fnode); + return (SDRC_ERROR); + } - if (sr->arg.dirop.fqi.fq_fattr.sa_vattr.va_type != VDIR) { - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + if (fqi->fq_fattr.sa_vattr.va_type != VDIR) { smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, ERRDOS, ERROR_PATH_NOT_FOUND); + smb_node_release(fqi->fq_fnode); return (SDRC_ERROR); } - rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE); + rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, FILE_TRAVERSE); - smb_node_release(dnode); - SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); + smb_node_release(fqi->fq_fnode); if (rc != 0) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, diff --git a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c index d4e7747c83..75b42e37bb 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c +++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c @@ -1158,7 +1158,7 @@ smbsr_lookup_xa(smb_request_t *sr) } void -smbsr_disconnect_file(smb_request_t *sr) +smbsr_release_file(smb_request_t *sr) { smb_ofile_t *of = sr->fid_ofile; diff --git a/usr/src/uts/common/fs/smbsrv/smb_find.c b/usr/src/uts/common/fs/smbsrv/smb_find.c index 99ec45c8e4..ec55d1e6ba 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_find.c +++ b/usr/src/uts/common/fs/smbsrv/smb_find.c @@ -340,7 +340,7 @@ smb_com_search(smb_request_t *sr) fileinfo.fi_name83, fileinfo.fi_name83+9, index, odid, client_key, fileinfo.fi_dosattr & 0xff, - smb_gmt2local(sr, fileinfo.fi_mtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec), (int32_t)fileinfo.fi_size, name); @@ -476,7 +476,7 @@ smb_com_find(smb_request_t *sr) fileinfo.fi_name83, fileinfo.fi_name83+9, index, odid, client_key, fileinfo.fi_dosattr & 0xff, - smb_gmt2local(sr, fileinfo.fi_mtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec), (int32_t)fileinfo.fi_size, name); @@ -654,7 +654,7 @@ smb_com_find_unique(struct smb_request *sr) fileinfo.fi_name83, fileinfo.fi_name83+9, index, odid, client_key, fileinfo.fi_dosattr & 0xff, - smb_gmt2local(sr, fileinfo.fi_mtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec), (int32_t)fileinfo.fi_size, name); diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c index 358fc93c3a..c427df5632 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c +++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c @@ -340,6 +340,8 @@ smb_fsop_create(smb_request_t *sr, cred_t *cr, smb_node_t *dnode, flags = SMB_IGNORE_CASE; if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; if (smb_is_stream_name(name)) { fname = kmem_alloc(MAXNAMELEN, KM_SLEEP); @@ -358,7 +360,8 @@ smb_fsop_create(smb_request_t *sr, cred_t *cr, smb_node_t *dnode, if (smb_maybe_mangled_name(name)) { longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - rc = smb_unmangle_name(dnode, name, longname, MAXNAMELEN); + rc = smb_unmangle_name(dnode, name, longname, + MAXNAMELEN, flags); kmem_free(longname, MAXNAMELEN); if (rc == 0) @@ -575,10 +578,13 @@ smb_fsop_mkdir( return (EROFS); if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; if (smb_maybe_mangled_name(name)) { longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - rc = smb_unmangle_name(dnode, name, longname, MAXNAMELEN); + rc = smb_unmangle_name(dnode, name, longname, + MAXNAMELEN, flags); kmem_free(longname, MAXNAMELEN); /* @@ -733,8 +739,11 @@ smb_fsop_remove( } longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; + rc = smb_unmangle_name(dnode, name, - longname, MAXNAMELEN); + longname, MAXNAMELEN, flags); if (rc == 0) { /* @@ -866,7 +875,11 @@ smb_fsop_rmdir( return (rc); longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - rc = smb_unmangle_name(dnode, name, longname, MAXNAMELEN); + + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; + rc = smb_unmangle_name(dnode, name, longname, + MAXNAMELEN, flags); if (rc == 0) { /* @@ -989,10 +1002,13 @@ smb_fsop_link(smb_request_t *sr, cred_t *cr, smb_node_t *to_dnode, flags = SMB_IGNORE_CASE; if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; if (smb_maybe_mangled_name(to_name)) { longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - rc = smb_unmangle_name(to_dnode, to_name, longname, MAXNAMELEN); + rc = smb_unmangle_name(to_dnode, to_name, + longname, MAXNAMELEN, flags); kmem_free(longname, MAXNAMELEN); if (rc == 0) @@ -1116,7 +1132,8 @@ smb_fsop_rename( * Please document any direct call to explain the reason * for avoiding this wrapper. * - * It is assumed that a reference exists on snode coming into this routine. + * It is assumed that a reference exists on snode coming into + * this function. * A null smb_request might be passed to this function. */ int @@ -1149,6 +1166,11 @@ smb_fsop_setattr( ACE_WRITE_ATTRIBUTES | ACE_WRITE_NAMED_ATTRS) == 0) return (EACCES); + /* + * The file system cannot detect pending READDONLY + * (i.e. if the file has been opened readonly but + * not yet closed) so we need to test READONLY here. + */ if (sr && (set_attr->sa_mask & SMB_AT_SIZE)) { if (sr->fid_ofile) { if (SMB_OFILE_IS_READONLY(sr->fid_ofile)) @@ -1159,14 +1181,29 @@ smb_fsop_setattr( } } - /* sr could be NULL in some cases */ + /* + * SMB checks access on open and retains an access granted + * mask for use while the file is open. ACL changes should + * not affect access to an open file. + * + * If the setattr is being performed on an ofile: + * - Check the ofile's access granted mask to see if the + * setattr is permitted. + * UID, GID - require WRITE_OWNER + * SIZE, ALLOCSZ - require FILE_WRITE_DATA + * all other attributes require FILE_WRITE_ATTRIBUTES + * + * - If the file system does access checking, set the + * ATTR_NOACLCHECK flag to ensure that the file system + * does not check permissions on subsequent calls. + */ if (sr && sr->fid_ofile) { sa_mask = set_attr->sa_mask; access = 0; - if (sa_mask & SMB_AT_SIZE) { + if (sa_mask & (SMB_AT_SIZE | SMB_AT_ALLOCSZ)) { access |= FILE_WRITE_DATA; - sa_mask &= ~SMB_AT_SIZE; + sa_mask &= ~(SMB_AT_SIZE | SMB_AT_ALLOCSZ); } if (sa_mask & (SMB_AT_UID|SMB_AT_GID)) { @@ -1645,6 +1682,8 @@ smb_fsop_lookup( flags |= SMB_IGNORE_CASE; if (SMB_TREE_SUPPORTS_CATIA(sr)) flags |= SMB_CATIA; + if (SMB_TREE_SUPPORTS_ABE(sr)) + flags |= SMB_ABE; od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP); @@ -1658,7 +1697,8 @@ smb_fsop_lookup( } longname = kmem_alloc(MAXNAMELEN, KM_SLEEP); - rc = smb_unmangle_name(dnode, name, longname, MAXNAMELEN); + rc = smb_unmangle_name(dnode, name, longname, + MAXNAMELEN, flags); if (rc != 0) { kmem_free(od_name, MAXNAMELEN); kmem_free(longname, MAXNAMELEN); diff --git a/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c b/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c index e8b17c6d96..2d73bb38ac 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c +++ b/usr/src/uts/common/fs/smbsrv/smb_mangle_name.c @@ -664,7 +664,8 @@ int smb_mangle_name( */ #define SMB_UNMANGLE_BUFSIZE (4 * 1024) int -smb_unmangle_name(smb_node_t *dnode, char *name, char *namebuf, int buflen) +smb_unmangle_name(smb_node_t *dnode, char *name, char *namebuf, + int buflen, uint32_t flags) { int err, eof, bufsize, reclen; uint64_t offset; @@ -700,7 +701,7 @@ smb_unmangle_name(smb_node_t *dnode, char *name, char *namebuf, int buflen) offset = 0; while ((err = smb_vop_readdir(vp, offset, buf, &bufsize, - &eof, kcred)) == 0) { + &eof, flags, kcred)) == 0) { if (bufsize == 0) { err = ENOENT; break; diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c index 7e0ee57629..3eb054e383 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c +++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c @@ -29,6 +29,9 @@ #include <smbsrv/smb_incl.h> +#include <sys/sunddi.h> + + #define MALLOC_QUANTUM 80 #define DECODE_NO_ERROR 0 @@ -36,6 +39,7 @@ #define DECODE_ALLOCATION_ERROR 2 #define DECODE_CONVERSION_ERROR 3 +static int mbc_marshal_cstou8(char *, char *, size_t, char *, size_t); static int mbc_marshal_make_room(mbuf_chain_t *, int32_t); static void mbc_marshal_store_byte(mbuf_chain_t *, uint8_t); static int mbc_marshal_put_char(mbuf_chain_t *mbc, uint8_t); @@ -323,7 +327,7 @@ unicode_translation: if (mbc_marshal_get_short(mbc, (uint16_t *)&d) != 0) return (-1); - *lvalp++ = smb_dos_to_ux_time(d, t); + *lvalp++ = smb_time_dos_to_unix(d, t); } break; @@ -338,7 +342,7 @@ unicode_translation: if (mbc_marshal_get_short(mbc, (uint16_t *)&t) != 0) return (-1); - *lvalp++ = smb_dos_to_ux_time(d, t); + *lvalp++ = smb_time_dos_to_unix(d, t); } break; @@ -554,7 +558,7 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap) case 'T': tvp = va_arg(ap, timestruc_t *); - nt_time = unix_to_nt_time(tvp); + nt_time = smb_time_unix_to_nt(tvp); if (mbc_marshal_put_long_long(mbc, nt_time) != 0) return (DECODE_NO_MORE_DATA); break; @@ -653,7 +657,7 @@ ascii_conversion: cvalp = va_arg(ap, uint8_t *); uint16_t d, t; lval = va_arg(ap, uint32_t); - (void) smb_ux_to_dos_time(lval, + smb_time_unix_to_dos(lval, (short *)&d, (short *)&t); if (mbc_marshal_put_short(mbc, t) != 0) return (DECODE_NO_MORE_DATA); @@ -667,7 +671,7 @@ ascii_conversion: cvalp = va_arg(ap, uint8_t *); uint16_t d, t; lval = va_arg(ap, uint32_t); - (void) smb_ux_to_dos_time(lval, + smb_time_unix_to_dos(lval, (short *)&d, (short *)&t); if (mbc_marshal_put_short(mbc, d) != 0) return (DECODE_NO_MORE_DATA); @@ -1218,10 +1222,8 @@ mbc_marshal_get_ascii_string( { char *rcvbuf; char *ch; - mts_wchar_t *wtmpbuf; int max; int length = 0; - uint_t cpid = oem_get_smb_cpid(); max = MALLOC_QUANTUM; rcvbuf = smbsr_malloc(ml, max); @@ -1254,15 +1256,9 @@ multibyte_encode: * UTF-8 encode the string for internal system use. */ length = strlen(rcvbuf) + 1; - wtmpbuf = smbsr_malloc(ml, length*sizeof (mts_wchar_t)); *ascii = smbsr_malloc(ml, length * MTS_MB_CHAR_MAX); - - if (oemstounicodes(wtmpbuf, rcvbuf, length, cpid) > 0) - (void) mts_wcstombs((char *)*ascii, wtmpbuf, - length * MTS_MB_CHAR_MAX); - else - (void) mts_stombs((char *)*ascii, rcvbuf, length * 2); - return (0); + return (mbc_marshal_cstou8("CP850", (char *)*ascii, + (size_t)length * MTS_MB_CHAR_MAX, rcvbuf, (size_t)length)); } static int @@ -1403,3 +1399,29 @@ mbc_marshal_get_skip(mbuf_chain_t *mbc, uint_t skip) mbc->chain_offset += skip; return (0); } + +/* + * Converts oem string to UTF-8 string with an output string of max + * maxconv bytes. The string may be truncated or not null-terminated if + * there is not enough room. + * + * returns -1, cnt (partial conversion) or 0 (success) + */ + +static int +mbc_marshal_cstou8(char *cs, char *outbuf, size_t maxconv, + char *inbuf, size_t srcbytes) +{ + kiconv_t t2u; + size_t inlen = srcbytes; + size_t outlen = maxconv; + int err = 0; + size_t rc; + + if ((t2u = kiconv_open("UTF-8", cs)) == (kiconv_t)-1) + return (-1); + + rc = kiconv(t2u, &inbuf, &inlen, &outbuf, &outlen, &err); + (void) kiconv_close(t2u); + return ((int)rc); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c index 3b0da29344..4df9749094 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_node.c +++ b/usr/src/uts/common/fs/smbsrv/smb_node.c @@ -145,16 +145,28 @@ static void smb_node_free(smb_node_t *); static int smb_node_constructor(void *, void *, int); static void smb_node_destructor(void *, void *); static smb_llist_t *smb_node_get_hash(fsid_t *, smb_attr_t *, uint32_t *); -static void smb_node_init_cached_timestamps(smb_node_t *); + +static void smb_node_init_cached_data(smb_node_t *); +static void smb_node_clear_cached_data(smb_node_t *); + +static void smb_node_init_cached_timestamps(smb_node_t *, smb_attr_t *); static void smb_node_clear_cached_timestamps(smb_node_t *); static void smb_node_get_cached_timestamps(smb_node_t *, smb_attr_t *); static void smb_node_set_cached_timestamps(smb_node_t *, smb_attr_t *); +static void smb_node_init_cached_allocsz(smb_node_t *, smb_attr_t *); +static void smb_node_clear_cached_allocsz(smb_node_t *); +static void smb_node_get_cached_allocsz(smb_node_t *, smb_attr_t *); +static void smb_node_set_cached_allocsz(smb_node_t *, smb_attr_t *); + #define VALIDATE_DIR_NODE(_dir_, _node_) \ ASSERT((_dir_)->n_magic == SMB_NODE_MAGIC); \ ASSERT(((_dir_)->vp->v_xattrdir) || ((_dir_)->vp->v_type == VDIR)); \ ASSERT((_dir_)->n_dnode != (_node_)); +/* round sz to DEV_BSIZE block */ +#define SMB_ALLOCSZ(sz) (((sz) + DEV_BSIZE-1) & ~(DEV_BSIZE-1)) + static kmem_cache_t *smb_node_cache = NULL; static boolean_t smb_node_initialized = B_FALSE; static smb_llist_t smb_node_hash_table[SMBND_HASH_MASK+1]; @@ -898,7 +910,7 @@ smb_node_inc_open_ofiles(smb_node_t *node) node->n_open_count++; mutex_exit(&node->n_mutex); - smb_node_init_cached_timestamps(node); + smb_node_init_cached_data(node); } /* @@ -913,7 +925,7 @@ smb_node_dec_open_ofiles(smb_node_t *node) node->n_open_count--; mutex_exit(&node->n_mutex); - smb_node_clear_cached_timestamps(node); + smb_node_clear_cached_data(node); } uint32_t @@ -1135,6 +1147,7 @@ smb_node_file_is_readonly(smb_node_t *node) * The ofile may be NULL, for example when a client request * specifies the file by pathname. * + * Timestamps * When attributes are set on an ofile, any pending timestamps * from a write request on the ofile are implicitly set to "now". * For compatibility with windows the following timestamps are @@ -1142,13 +1155,21 @@ smb_node_file_is_readonly(smb_node_t *node) * - if any attribute is being explicitly set, set ctime to now * - if file size is being explicitly set, set atime & ctime to now * - * Any attribute that is being explicitly set, or has previously + * Any timestamp that is being explicitly set, or has previously * been explicitly set on the ofile, is excluded from implicit * (now) setting. * * Updates the node's cached timestamp values. * Updates the ofile's explicit times flag. * + * File allocation size + * When the file allocation size is set it is first rounded up + * to block size. If the file size is smaller than the allocation + * size the file is truncated by setting the filesize to allocsz. + * If there are open ofiles, the allocsz is cached on the node. + * + * Updates the node's cached allocsz value. + * * Returns: errno */ int @@ -1156,56 +1177,77 @@ smb_node_setattr(smb_request_t *sr, smb_node_t *node, cred_t *cr, smb_ofile_t *of, smb_attr_t *attr) { int rc; - uint32_t what; - uint32_t now_times = 0; + uint32_t pending_times = 0; + uint32_t explicit_times = 0; timestruc_t now; + smb_attr_t tmp_attr; ASSERT(attr); SMB_NODE_VALID(node); - what = attr->sa_mask; + /* set attributes specified in attr */ + if (attr->sa_mask != 0) { + /* if allocation size is < file size, truncate the file */ + if (attr->sa_mask & SMB_AT_ALLOCSZ) { + attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_allocsz); - /* determine which timestamps to implicitly set to "now" */ - if (what) - now_times |= SMB_AT_CTIME; - if (what & SMB_AT_SIZE) - now_times |= (SMB_AT_MTIME | SMB_AT_CTIME); - if (of) { - if (smb_ofile_write_time_pending(of)) - now_times |= - (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME); - now_times &= ~(smb_ofile_explicit_times(of)); - } - now_times &= ~what; + bzero(&tmp_attr, sizeof (smb_attr_t)); + tmp_attr.sa_mask = SMB_AT_SIZE; + (void) smb_fsop_getattr(NULL, kcred, node, &tmp_attr); - if (now_times) { - gethrestime(&now); - - if (now_times & SMB_AT_ATIME) { - attr->sa_vattr.va_atime = now; - attr->sa_mask |= SMB_AT_ATIME; - } - if (now_times & SMB_AT_MTIME) { - attr->sa_vattr.va_mtime = now; - attr->sa_mask |= SMB_AT_MTIME; + if (tmp_attr.sa_vattr.va_size > attr->sa_allocsz) { + attr->sa_vattr.va_size = attr->sa_allocsz; + attr->sa_mask |= SMB_AT_SIZE; + } } - if (now_times & SMB_AT_CTIME) { - attr->sa_vattr.va_ctime = now; - attr->sa_mask |= SMB_AT_CTIME; + + rc = smb_fsop_setattr(sr, cr, node, attr); + if (rc != 0) + return (rc); + + smb_node_set_cached_allocsz(node, attr); + smb_node_set_cached_timestamps(node, attr); + if (of) { + smb_ofile_set_explicit_times(of, + (attr->sa_mask & SMB_AT_TIMES)); } } - if (attr->sa_mask == 0) - return (0); + /* + * Determine which timestamps to implicitly set to "now". + * Don't overwrite timestamps already explicitly set. + */ + bzero(&tmp_attr, sizeof (smb_attr_t)); + gethrestime(&now); + tmp_attr.sa_vattr.va_atime = now; + tmp_attr.sa_vattr.va_mtime = now; + tmp_attr.sa_vattr.va_ctime = now; - rc = smb_fsop_setattr(sr, cr, node, attr); - if (rc != 0) - return (rc); + /* pending write timestamps */ + if (of) { + if (smb_ofile_write_time_pending(of)) { + pending_times |= + (SMB_AT_MTIME | SMB_AT_CTIME | SMB_AT_ATIME); + } + explicit_times |= (smb_ofile_explicit_times(of)); + } + explicit_times |= (attr->sa_mask & SMB_AT_TIMES); + pending_times &= ~explicit_times; - smb_node_set_cached_timestamps(node, attr); + if (pending_times) { + tmp_attr.sa_mask = pending_times; + (void) smb_fsop_setattr(NULL, kcred, node, &tmp_attr); + } - if (of) - smb_ofile_set_explicit_times(of, (what & SMB_AT_TIMES)); + /* additional timestamps to update in cache */ + if (attr->sa_mask) + tmp_attr.sa_mask |= SMB_AT_CTIME; + if (attr->sa_mask & (SMB_AT_SIZE | SMB_AT_ALLOCSZ)) + tmp_attr.sa_mask |= SMB_AT_MTIME; + tmp_attr.sa_mask &= ~explicit_times; + + if (tmp_attr.sa_mask) + smb_node_set_cached_timestamps(node, &tmp_attr); return (0); } @@ -1238,21 +1280,136 @@ smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr) mutex_enter(&node->n_mutex); - if (node->vp->v_type == VDIR) + if (node->vp->v_type == VDIR) { attr->sa_vattr.va_size = 0; + attr->sa_allocsz = 0; + } if (node->readonly_creator) attr->sa_dosattr |= FILE_ATTRIBUTE_READONLY; if (attr->sa_dosattr == 0) attr->sa_dosattr = FILE_ATTRIBUTE_NORMAL; + mutex_exit(&node->n_mutex); + smb_node_get_cached_allocsz(node, attr); smb_node_get_cached_timestamps(node, attr); + return (0); } /* + * smb_node_init_cached_data + */ +static void +smb_node_init_cached_data(smb_node_t *node) +{ + smb_attr_t attr; + + bzero(&attr, sizeof (smb_attr_t)); + attr.sa_mask = SMB_AT_ALL; + (void) smb_fsop_getattr(NULL, kcred, node, &attr); + + smb_node_init_cached_allocsz(node, &attr); + smb_node_init_cached_timestamps(node, &attr); +} + +/* + * smb_node_clear_cached_data + */ +static void +smb_node_clear_cached_data(smb_node_t *node) +{ + smb_node_clear_cached_allocsz(node); + smb_node_clear_cached_timestamps(node); +} + +/* + * File allocation size (allocsz) caching + * + * When there are open ofiles on the node, the file allocsz is cached. + * The cached value (n_allocsz) is initialized when the first ofile is + * opened and cleared when the last is closed. Allocsz calculated from + * the filesize (rounded up to block size). + * When the allocation size is queried, if the cached allocsz is less + * than the filesize, it is recalculated from the filesize. + */ + +/* + * smb_node_init_cached_allocsz + * + * If there are open ofiles, cache the allocsz in the node. + * Calculate the allocsz from the filesizes. + * block size). + */ +static void +smb_node_init_cached_allocsz(smb_node_t *node, smb_attr_t *attr) +{ + mutex_enter(&node->n_mutex); + if (node->n_open_count == 1) + node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); + mutex_exit(&node->n_mutex); +} + +/* + * smb_node_clear_cached_allocsz + */ +static void +smb_node_clear_cached_allocsz(smb_node_t *node) +{ + mutex_enter(&node->n_mutex); + if (node->n_open_count == 0) + node->n_allocsz = 0; + mutex_exit(&node->n_mutex); +} + +/* + * smb_node_get_cached_allocsz + * + * If there is no cached allocsz (no open files), calculate + * allocsz from the filesize. + * If the allocsz is cached but is smaller than the filesize + * recalculate the cached allocsz from the filesize. + * + * Return allocs in attr->sa_allocsz. + */ +static void +smb_node_get_cached_allocsz(smb_node_t *node, smb_attr_t *attr) +{ + if (node->vp->v_type == VDIR) + return; + + mutex_enter(&node->n_mutex); + if (node->n_open_count == 0) { + attr->sa_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); + } else { + if (node->n_allocsz < attr->sa_vattr.va_size) + node->n_allocsz = SMB_ALLOCSZ(attr->sa_vattr.va_size); + attr->sa_allocsz = node->n_allocsz; + } + mutex_exit(&node->n_mutex); +} + +/* + * smb_node_set_cached_allocsz + * + * attr->sa_allocsz has already been rounded to block size by + * the caller. + */ +static void +smb_node_set_cached_allocsz(smb_node_t *node, smb_attr_t *attr) +{ + mutex_enter(&node->n_mutex); + if (attr->sa_mask & SMB_AT_ALLOCSZ) { + if (node->n_open_count > 0) + node->n_allocsz = attr->sa_allocsz; + } + mutex_exit(&node->n_mutex); +} + + +/* * Timestamp caching * * Solaris file systems handle timestamps different from NTFS. For @@ -1282,10 +1439,9 @@ smb_node_getattr(smb_request_t *sr, smb_node_t *node, smb_attr_t *attr) * file system values. */ static void -smb_node_init_cached_timestamps(smb_node_t *node) +smb_node_init_cached_timestamps(smb_node_t *node, smb_attr_t *attr) { smb_node_t *unode; - smb_attr_t attr; if ((unode = SMB_IS_STREAM(node)) != NULL) node = unode; @@ -1293,13 +1449,10 @@ smb_node_init_cached_timestamps(smb_node_t *node) mutex_enter(&node->n_mutex); ++(node->n_timestamps.t_open_ofiles); if (node->n_timestamps.t_open_ofiles == 1) { - bzero(&attr, sizeof (smb_attr_t)); - attr.sa_mask = SMB_AT_TIMES; - (void) smb_fsop_getattr(NULL, kcred, node, &attr); - node->n_timestamps.t_mtime = attr.sa_vattr.va_mtime; - node->n_timestamps.t_atime = attr.sa_vattr.va_atime; - node->n_timestamps.t_ctime = attr.sa_vattr.va_ctime; - node->n_timestamps.t_crtime = attr.sa_crtime; + node->n_timestamps.t_mtime = attr->sa_vattr.va_mtime; + node->n_timestamps.t_atime = attr->sa_vattr.va_atime; + node->n_timestamps.t_ctime = attr->sa_vattr.va_ctime; + node->n_timestamps.t_crtime = attr->sa_crtime; node->n_timestamps.t_cached = B_TRUE; } mutex_exit(&node->n_mutex); diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c index 36300171cf..aa12b2c96b 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_create_andx.c @@ -269,7 +269,7 @@ smb_com_nt_create_andx(struct smb_request *sr) } op->fqi.fq_dnode = sr->fid_ofile->f_node; - smbsr_disconnect_file(sr); + smbsr_release_file(sr); } if (smb_common_open(sr) != NT_STATUS_SUCCESS) @@ -315,7 +315,7 @@ smb_com_nt_create_andx(struct smb_request *sr) &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr & FILE_ATTRIBUTE_MASK, - attr.sa_vattr.va_size, + attr.sa_allocsz, attr.sa_vattr.va_size, op->ftype, op->devstate, diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c index f0791b5573..80f2319a96 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_create.c @@ -185,7 +185,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa) } op->fqi.fq_dnode = sr->fid_ofile->f_node; - smbsr_disconnect_file(sr); + smbsr_release_file(sr); } status = smb_common_open(sr); @@ -231,7 +231,7 @@ smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa) &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr & FILE_ATTRIBUTE_MASK, - attr.sa_vattr.va_size, + attr.sa_allocsz, attr.sa_vattr.va_size, op->ftype, op->devstate, diff --git a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c index 68f86a155c..d1290e5889 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c +++ b/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c @@ -27,29 +27,30 @@ #include <smbsrv/winioctl.h> #include <smbsrv/ntstatus.h> + +static uint32_t smb_nt_trans_ioctl_noop(smb_request_t *, smb_xa_t *); static uint32_t smb_nt_trans_ioctl_invalid_parm(smb_request_t *, smb_xa_t *); /* - * This table defines the list of IOCTL/FSCTL values for which we'll - * call a funtion to return a specific processing. + * This table defines the list of FSCTL values for which we'll + * call a funtion to perform specific processing. */ static struct { uint32_t fcode; uint32_t (*ioctl_func)(smb_request_t *sr, smb_xa_t *xa); } ioctl_ret_tbl[] = { - { FSCTL_GET_OBJECT_ID, smb_nt_trans_ioctl_invalid_parm }, - { FSCTL_QUERY_ALLOCATED_RANGES, smb_nt_trans_ioctl_invalid_parm }, - { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_vss_ioctl_enumerate_snaps } + { FSCTL_GET_OBJECT_ID, smb_nt_trans_ioctl_invalid_parm }, + { FSCTL_QUERY_ALLOCATED_RANGES, smb_nt_trans_ioctl_invalid_parm }, + { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_vss_ioctl_enumerate_snaps }, + { FSCTL_SET_SPARSE, smb_nt_trans_ioctl_noop } }; /* * smb_nt_transact_ioctl * * This command allows device and file system control functions to be - * transferred transparently from client to server. This is currently - * a stub to work out whether or not we need to return an NT status - * code. + * transferred transparently from client to server. * * Setup Words Encoding Description * =========================== ========================================= @@ -78,7 +79,7 @@ static struct { smb_sdrc_t smb_nt_transact_ioctl(smb_request_t *sr, smb_xa_t *xa) { - uint32_t status = NT_STATUS_SUCCESS; + uint32_t status = NT_STATUS_NOT_SUPPORTED; uint32_t fcode; unsigned short fid; unsigned char is_fsctl; @@ -91,6 +92,10 @@ smb_nt_transact_ioctl(smb_request_t *sr, smb_xa_t *xa) return (SDRC_ERROR); } + /* + * Invoke handler if specified, otherwise the default + * behavior is to return NT_STATUS_NOT_SUPPORTED + */ for (i = 0; i < sizeof (ioctl_ret_tbl) / sizeof (ioctl_ret_tbl[0]); i++) { if (ioctl_ret_tbl[i].fcode == fcode) { @@ -110,6 +115,13 @@ smb_nt_transact_ioctl(smb_request_t *sr, smb_xa_t *xa) /* ARGSUSED */ static uint32_t +smb_nt_trans_ioctl_noop(smb_request_t *sr, smb_xa_t *xa) +{ + return (NT_STATUS_SUCCESS); +} + +/* ARGSUSED */ +static uint32_t smb_nt_trans_ioctl_invalid_parm(smb_request_t *sr, smb_xa_t *xa) { return (NT_STATUS_INVALID_PARAMETER); diff --git a/usr/src/uts/common/fs/smbsrv/smb_odir.c b/usr/src/uts/common/fs/smbsrv/smb_odir.c index 61e92b3366..c953aa05a5 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_odir.c +++ b/usr/src/uts/common/fs/smbsrv/smb_odir.c @@ -246,6 +246,7 @@ #include <smbsrv/smb_incl.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> +#include <smbsrv/smb_share.h> #include <sys/extdirent.h> /* static functions */ @@ -659,9 +660,8 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od, { int rc; smb_odirent_t *odirent; - vnode_t *vp; + smb_node_t *fnode; smb_attr_t attr; - int tmpflg; ASSERT(sr); ASSERT(sr->sr_magic == SMB_REQ_MAGIC); @@ -702,15 +702,11 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od, continue; } - /* - * since we only care about the size attributes we don't need - * to pass the vp of the unnamed stream file to smb_vop_getattr - */ - rc = smb_vop_lookup(od->d_dnode->vp, odirent->od_name, &vp, - NULL, 0, &tmpflg, od->d_tree->t_snode->vp, od->d_cred); + rc = smb_fsop_lookup(sr, od->d_cred, 0, od->d_tree->t_snode, + od->d_dnode, odirent->od_name, &fnode); if (rc == 0) { - rc = smb_vop_getattr(vp, NULL, &attr, 0, od->d_cred); - VN_RELE(vp); + rc = smb_node_getattr(sr, fnode, &attr); + smb_node_release(fnode); } if (rc == 0) { @@ -718,8 +714,7 @@ smb_odir_read_streaminfo(smb_request_t *sr, smb_odir_t *od, odirent->od_name + SMB_STREAM_PREFIX_LEN, sizeof (sinfo->si_name)); sinfo->si_size = attr.sa_vattr.va_size; - sinfo->si_alloc_size = - attr.sa_vattr.va_nblocks * DEV_BSIZE; + sinfo->si_alloc_size = attr.sa_allocsz; break; } } @@ -872,6 +867,8 @@ smb_odir_create(smb_request_t *sr, smb_node_t *dnode, od->d_flags |= SMB_ODIR_FLAG_IGNORE_CASE; 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; od->d_eof = B_FALSE; smb_llist_enter(&tree->t_odir_list, RW_WRITER); @@ -939,6 +936,7 @@ smb_odir_next_odirent(smb_odir_t *od, smb_odirent_t *odirent) dirent64_t *dp; edirent_t *edp; char *np; + uint32_t abe_flag = 0; ASSERT(MUTEX_HELD(&od->d_mutex)); @@ -965,8 +963,11 @@ smb_odir_next_odirent(smb_odir_t *od, smb_odirent_t *odirent) od->d_bufsize = sizeof (od->d_buf); + if (od->d_flags & SMB_ODIR_FLAG_ABE) + abe_flag = SMB_ABE; + rc = smb_vop_readdir(od->d_dnode->vp, od->d_offset, - od->d_buf, &od->d_bufsize, &eof, od->d_cred); + od->d_buf, &od->d_bufsize, &eof, abe_flag, od->d_cred); if ((rc == 0) && (od->d_bufsize == 0)) rc = ENOENT; @@ -1109,7 +1110,7 @@ smb_odir_single_fileinfo(smb_request_t *sr, smb_odir_t *od, 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_vattr.va_nblocks * DEV_BSIZE; + 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; @@ -1200,7 +1201,7 @@ smb_odir_wildcard_fileinfo(smb_request_t *sr, smb_odir_t *od, 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_vattr.va_nblocks * DEV_BSIZE; + 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; diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c index 56eb9b8c8d..8d7ec2f06d 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c +++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c @@ -764,6 +764,11 @@ smb_ofile_set_write_time_pending(smb_ofile_t *of) mutex_exit(&of->f_mutex); } +/* + * smb_ofile_write_time_pending + * + * Get and reset the write times pending flag. + */ boolean_t smb_ofile_write_time_pending(smb_ofile_t *of) { @@ -771,8 +776,10 @@ smb_ofile_write_time_pending(smb_ofile_t *of) SMB_OFILE_VALID(of); mutex_enter(&of->f_mutex); - if (of->f_flags & SMB_OFLAGS_TIMESTAMPS_PENDING) + if (of->f_flags & SMB_OFLAGS_TIMESTAMPS_PENDING) { rc = B_TRUE; + of->f_flags &= ~SMB_OFLAGS_TIMESTAMPS_PENDING; + } mutex_exit(&of->f_mutex); return (rc); @@ -782,16 +789,13 @@ smb_ofile_write_time_pending(smb_ofile_t *of) * smb_ofile_set_explicit_time_flag * * Note the timestamps specified in "what", as having been - * explicity set for the ofile. Also clear the flag for pending - * timestamps as the pending timestamps will have been applied - * by the explicit set. + * explicity set for the ofile. */ void smb_ofile_set_explicit_times(smb_ofile_t *of, uint32_t what) { SMB_OFILE_VALID(of); mutex_enter(&of->f_mutex); - of->f_flags &= ~SMB_OFLAGS_TIMESTAMPS_PENDING; of->f_explicit_times |= (what & SMB_AT_TIMES); mutex_exit(&of->f_mutex); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_open_andx.c b/usr/src/uts/common/fs/smbsrv/smb_open_andx.c index e0730d580f..f701f35c7b 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_open_andx.c +++ b/usr/src/uts/common/fs/smbsrv/smb_open_andx.c @@ -290,7 +290,7 @@ smb_com_open(smb_request_t *sr) 7, sr->smb_fid, file_attr, - smb_gmt2local(sr, attr.sa_vattr.va_mtime.tv_sec), + smb_time_gmt_to_local(sr, attr.sa_vattr.va_mtime.tv_sec), (uint32_t)op->dsize, op->omode, (uint16_t)0); /* bcc */ @@ -331,7 +331,8 @@ smb_pre_open_andx(smb_request_t *sr) op->op_oplock_level = SMB_OPLOCK_NONE; if ((creation_time != 0) && (creation_time != UINT_MAX)) - op->crtime.tv_sec = smb_local2gmt(sr, creation_time); + op->crtime.tv_sec = + smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->create_disposition = smb_ofun_to_crdisposition(op->ofun); @@ -399,7 +400,7 @@ smb_com_open_andx(smb_request_t *sr) sr->andx_com, VAR_BCC, sr->smb_fid, file_attr, - smb_gmt2local(sr, attr.sa_vattr.va_mtime.tv_sec), + smb_time_gmt_to_local(sr, attr.sa_vattr.va_mtime.tv_sec), (uint32_t)op->dsize, op->omode, op->ftype, op->devstate, @@ -442,7 +443,7 @@ smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa) return (SDRC_ERROR); if ((creation_time != 0) && (creation_time != UINT_MAX)) - op->crtime.tv_sec = smb_local2gmt(sr, creation_time); + op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->dattr = file_attr; diff --git a/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c b/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c index e585d4cf3c..cfc1956044 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c +++ b/usr/src/uts/common/fs/smbsrv/smb_path_name_reduction.c @@ -60,79 +60,6 @@ smb_is_executable(char *path) } /* - * smbd_fs_query - * - * Upon success, the caller will need to call smb_node_release() on - * fqi.fq_fnode (if it isn't already set to NULL by this routine) and - * and fqi.fq_dnode. These pointers will not be used after the caller - * is done with them and should be released immediately. (The position - * of smb_fqi in a union in the smb_request structure makes it difficult - * to free these pointers at smb_request deallocation time.) - * - * If smbd_fs_query() returns error, no smb_nodes will need to be released - * by callers as a result of references taken in this routine, and - * fqi.fq_fnode and fqi.fq_dnode will be set to NULL. - */ - -int -smbd_fs_query(smb_request_t *sr, smb_fqi_t *fqi, int fqm) -{ - int rc; - - rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path, - sr->tid_tree->t_snode, sr->tid_tree->t_snode, &fqi->fq_dnode, - fqi->fq_last_comp); - - if (rc) - return (rc); - - rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, fqi->fq_dnode, fqi->fq_last_comp, - &fqi->fq_fnode); - - if (rc == 0) { - (void) strcpy(fqi->fq_od_name, fqi->fq_fnode->od_name); - - /* - * fqi->fq_fattr MUST be set even if returning EEXIST, as it - * is used by some callers to determine how to handle EEXIST - */ - rc = smb_node_getattr(sr, fqi->fq_fnode, &fqi->fq_fattr); - if (rc != 0) { - smb_node_release(fqi->fq_dnode); - smb_node_release(fqi->fq_fnode); - SMB_NULL_FQI_NODES(*fqi); - return (rc); - } - - if (fqm == FQM_PATH_MUST_NOT_EXIST) { - smb_node_release(fqi->fq_dnode); - smb_node_release(fqi->fq_fnode); - SMB_NULL_FQI_NODES(*fqi); - return (EEXIST); - } - - return (0); - } - - if (fqm == FQM_PATH_MUST_EXIST) { - smb_node_release(fqi->fq_dnode); - SMB_NULL_FQI_NODES(*fqi); - return (rc); - } - - if (rc == ENOENT) { - fqi->fq_fnode = NULL; - return (0); - } - - smb_node_release(fqi->fq_dnode); - SMB_NULL_FQI_NODES(*fqi); - - return (rc); -} - -/* * smb_pathname_reduce * * smb_pathname_reduce() takes a path and returns the smb_node for the @@ -398,6 +325,7 @@ smb_pathname(smb_request_t *sr, char *path, int flags, int err = 0; int nlink = 0; int local_flags; + uint32_t abe_flag = 0; char namebuf[MAXNAMELEN]; if (path == NULL) @@ -419,6 +347,9 @@ smb_pathname(smb_request_t *sr, char *path, int flags, return (err); } + if (SMB_TREE_SUPPORTS_ABE(sr)) + abe_flag = SMB_ABE; + (void) pn_alloc(&pn); (void) pn_alloc(&rpn); @@ -458,7 +389,7 @@ smb_pathname(smb_request_t *sr, char *path, int flags, break; if ((err = smb_unmangle_name(dnode, component, - real_name, MAXNAMELEN)) != 0) + real_name, MAXNAMELEN, abe_flag)) != 0) break; if ((namep = smb_pathname_catia_v5tov4(sr, real_name, diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c new file mode 100644 index 0000000000..377baec57e --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c @@ -0,0 +1,891 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <smbsrv/smb_vops.h> +#include <smbsrv/smb_incl.h> +#include <smbsrv/smb_fsops.h> + +/* + * Trans2 Query File/Path Information Levels: + * + * SMB_INFO_STANDARD + * SMB_INFO_QUERY_EA_SIZE + * SMB_INFO_QUERY_EAS_FROM_LIST + * SMB_INFO_QUERY_ALL_EAS - not valid for pipes + * SMB_INFO_IS_NAME_VALID - only valid when query is by path + * + * SMB_QUERY_FILE_BASIC_INFO + * SMB_QUERY_FILE_STANDARD_INFO + * SMB_QUERY_FILE_EA_INFO + * SMB_QUERY_FILE_NAME_INFO + * SMB_QUERY_FILE_ALL_INFO + * SMB_QUERY_FILE_ALT_NAME_INFO - not valid for pipes + * SMB_QUERY_FILE_STREAM_INFO - not valid for pipes + * SMB_QUERY_FILE_COMPRESSION_INFO - not valid for pipes + * + * Supported Passthrough levels: + * SMB_FILE_BASIC_INFORMATION + * SMB_FILE_STANDARD_INFORMATION + * SMB_FILE_INTERNAL_INFORMATION + * SMB_FILE_EA_INFORMATION + * SMB_FILE_ACCESS_INFORMATION - not yet supported quen query by path + * SMB_FILE_NAME_INFORMATION + * SMB_FILE_ALL_INFORMATION + * SMB_FILE_ALT_NAME_INFORMATION - not valid for pipes + * SMB_FILE_STREAM_INFORMATION - not valid for pipes + * SMB_FILE_COMPRESSION_INFORMATION - not valid for pipes + * SMB_FILE_ATTR_TAG_INFORMATION - not valid for pipes + * + * Internal levels representing non trans2 requests + * SMB_QUERY_INFORMATION + * SMB_QUERY_INFORMATION2 + */ + +typedef struct smb_queryinfo { + smb_node_t *qi_node; /* NULL for pipes */ + smb_attr_t qi_attr; + boolean_t qi_delete_on_close; + uint32_t qi_namelen; + char qi_name83[SMB_SHORTNAMELEN]; + char qi_shortname[SMB_SHORTNAMELEN]; + char qi_name[MAXPATHLEN]; +} smb_queryinfo_t; +#define qi_mtime qi_attr.sa_vattr.va_mtime +#define qi_ctime qi_attr.sa_vattr.va_ctime +#define qi_atime qi_attr.sa_vattr.va_atime +#define qi_crtime qi_attr.sa_crtime + +static int smb_query_by_fid(smb_request_t *, smb_xa_t *, uint16_t); +static int smb_query_by_path(smb_request_t *, smb_xa_t *, + uint16_t, char *); + +static int smb_query_fileinfo(smb_request_t *, smb_node_t *, + uint16_t, smb_queryinfo_t *); +static int smb_query_pipeinfo(smb_request_t *, smb_opipe_t *, + uint16_t, smb_queryinfo_t *); +static boolean_t smb_query_pipe_valid_infolev(smb_request_t *, uint16_t); + +static int smb_query_encode_response(smb_request_t *, smb_xa_t *, + uint16_t, smb_queryinfo_t *); +static void smb_encode_stream_info(smb_request_t *, smb_xa_t *, + smb_queryinfo_t *); +static int smb_all_info_filename(smb_tree_t *, smb_node_t *, char *, size_t); +uint32_t smb_pad_align(uint32_t offset, uint32_t align); + + +/* + * smb_com_trans2_query_file_information + */ +smb_sdrc_t +smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa) +{ + uint16_t infolev; + + if (smb_mbc_decodef(&xa->req_param_mb, "ww", + &sr->smb_fid, &infolev) != 0) + return (SDRC_ERROR); + + if (smb_query_by_fid(sr, xa, infolev) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * 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); +} + +/* + * smb_com_query_information (aka getattr) + */ +smb_sdrc_t +smb_pre_query_information(smb_request_t *sr) +{ + int rc; + smb_fqi_t *fqi = &sr->arg.dirop.fqi; + + rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path); + + DTRACE_SMB_2(op__QueryInformation__start, smb_request_t *, sr, + smb_fqi_t *, fqi); + + return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); +} + +void +smb_post_query_information(smb_request_t *sr) +{ + DTRACE_SMB_1(op__QueryInformation__done, smb_request_t *, sr); +} + +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); +} + +/* + * smb_com_query_information2 (aka getattre) + */ +smb_sdrc_t +smb_pre_query_information2(smb_request_t *sr) +{ + int rc; + rc = smbsr_decode_vwv(sr, "w", &sr->smb_fid); + + DTRACE_SMB_1(op__QueryInformation2__start, smb_request_t *, sr); + + return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); +} + +void +smb_post_query_information2(smb_request_t *sr) +{ + DTRACE_SMB_1(op__QueryInformation2__done, smb_request_t *, sr); +} + +smb_sdrc_t +smb_com_query_information2(smb_request_t *sr) +{ + uint16_t infolev = SMB_QUERY_INFORMATION2; + + if (smb_query_by_fid(sr, NULL, infolev) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * smb_query_by_fid + * + * Common code for querying file information by open file (or pipe) id. + * Use the id to identify the node / pipe object and request the + * smb_queryinfo_t data for that object. + */ +static int +smb_query_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev) +{ + int rc; + smb_queryinfo_t *qinfo; + smb_node_t *node; + smb_opipe_t *opipe; + + smbsr_lookup_file(sr); + + if (sr->fid_ofile == NULL) { + smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); + return (-1); + } + + if (infolev == SMB_INFO_IS_NAME_VALID) { + smbsr_error(sr, 0, ERRDOS, ERRunknownlevel); + smbsr_release_file(sr); + return (-1); + } + + if ((sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE) && + (!smb_query_pipe_valid_infolev(sr, infolev))) { + smbsr_release_file(sr); + return (-1); + } + + qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP); + + switch (sr->fid_ofile->f_ftype) { + case SMB_FTYPE_DISK: + node = sr->fid_ofile->f_node; + rc = smb_query_fileinfo(sr, node, infolev, qinfo); + break; + case SMB_FTYPE_MESG_PIPE: + opipe = sr->fid_ofile->f_pipe; + rc = smb_query_pipeinfo(sr, opipe, infolev, qinfo); + break; + default: + smbsr_error(sr, 0, ERRDOS, ERRbadfile); + rc = -1; + break; + } + + if (rc == 0) + rc = smb_query_encode_response(sr, xa, infolev, qinfo); + + kmem_free(qinfo, sizeof (smb_queryinfo_t)); + smbsr_release_file(sr); + return (rc); +} + +/* + * smb_query_by_path + * + * Common code for querying file information by file name. + * Use the file name to identify the node object and request the + * smb_queryinfo_t data for that node. + * + * Querying attributes on a named pipe by name is an error and + * is handled in the calling functions so that they can return + * the appropriate error status code (which differs by caller). + */ +static int +smb_query_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev, char *path) +{ + smb_queryinfo_t *qinfo; + smb_node_t *node, *dnode; + int rc; + int len; + + /* VALID, but not yet supported */ + if (infolev == SMB_FILE_ACCESS_INFORMATION) { + smbsr_error(sr, 0, ERRDOS, ERRunknownlevel); + return (-1); + } + + /* + * Some MS clients pass NULL file names. NT interprets this as "\". + * Otherwise, if path is not "\\", remove the terminating slash. + */ + if ((len = strlen(path)) == 0) + path = "\\"; + else { + if ((len > 1) && (path[len - 1] == '\\')) { + path[len - 1] = 0; + } + } + + qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP); + + rc = smb_pathname_reduce(sr, sr->user_cr, path, sr->tid_tree->t_snode, + sr->tid_tree->t_snode, &dnode, qinfo->qi_name); + if (rc == 0) { + rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dnode, qinfo->qi_name, &node); + smb_node_release(dnode); + } + + 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); + + kmem_free(qinfo, sizeof (smb_queryinfo_t)); + return (-1); + } + + rc = smb_query_fileinfo(sr, node, infolev, qinfo); + if (rc != 0) { + kmem_free(qinfo, sizeof (smb_queryinfo_t)); + smb_node_release(node); + return (rc); + } + + /* If delete_on_close - NT_STATUS_DELETE_PENDING */ + if (qinfo->qi_delete_on_close) { + smbsr_error(sr, NT_STATUS_DELETE_PENDING, + ERRDOS, ERROR_ACCESS_DENIED); + kmem_free(qinfo, sizeof (smb_queryinfo_t)); + smb_node_release(node); + return (-1); + } + + rc = smb_query_encode_response(sr, xa, infolev, qinfo); + kmem_free(qinfo, sizeof (smb_queryinfo_t)); + smb_node_release(node); + return (rc); +} + +/* + * smb_size32 + * Some responses only support 32 bit file sizes. If the file size + * exceeds UINT_MAX (32 bit) we return UINT_MAX in the response. + */ +static uint32_t +smb_size32(u_offset_t size) +{ + return ((size > UINT_MAX) ? UINT_MAX : (uint32_t)size); +} + +/* + * smb_query_encode_response + * + * Encode the data from smb_queryinfo_t into client response + */ +int +smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa, + uint16_t infolev, smb_queryinfo_t *qinfo) +{ + uint16_t dattr; + u_offset_t datasz, allocsz; + + dattr = qinfo->qi_attr.sa_dosattr & FILE_ATTRIBUTE_MASK; + datasz = qinfo->qi_attr.sa_vattr.va_size; + allocsz = qinfo->qi_attr.sa_allocsz; + + switch (infolev) { + case SMB_QUERY_INFORMATION: + (void) smbsr_encode_result(sr, 10, 0, "bwll10.w", + 10, + dattr, + smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec), + smb_size32(datasz), + 0); + break; + + case SMB_QUERY_INFORMATION2: + (void) smbsr_encode_result(sr, 11, 0, "byyyllww", + 11, + smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec), + smb_size32(datasz), smb_size32(allocsz), dattr, 0); + break; + + case SMB_FILE_ACCESS_INFORMATION: + ASSERT(sr->fid_ofile); + (void) smb_mbc_encodef(&xa->rep_data_mb, "l", + sr->fid_ofile->f_granted_access); + break; + + case SMB_INFO_STANDARD: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) ? + "YYYllw" : "yyyllw"), + smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec), + smb_size32(datasz), smb_size32(allocsz), dattr); + break; + + case SMB_INFO_QUERY_EA_SIZE: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, + ((sr->session->native_os == NATIVE_OS_WIN95) ? + "YYYllwl" : "yyyllwl"), + smb_time_gmt_to_local(sr, qinfo->qi_crtime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_atime.tv_sec), + smb_time_gmt_to_local(sr, qinfo->qi_mtime.tv_sec), + smb_size32(datasz), smb_size32(allocsz), dattr, 0); + break; + + case SMB_INFO_QUERY_ALL_EAS: + case SMB_INFO_QUERY_EAS_FROM_LIST: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); + break; + + case SMB_INFO_IS_NAME_VALID: + break; + + case SMB_QUERY_FILE_BASIC_INFO: + case SMB_FILE_BASIC_INFORMATION: + /* + * NT includes 6 bytes (spec says 4) at the end of this + * response, which are required by NetBench 5.01. + */ + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.", + &qinfo->qi_crtime, + &qinfo->qi_atime, + &qinfo->qi_mtime, + &qinfo->qi_ctime, + dattr); + break; + + case SMB_QUERY_FILE_STANDARD_INFO: + case SMB_FILE_STANDARD_INFORMATION: + /* 2-byte pad at end */ + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "qqlbb2.", + (uint64_t)allocsz, + (uint64_t)datasz, + qinfo->qi_attr.sa_vattr.va_nlink, + qinfo->qi_delete_on_close, + (qinfo->qi_attr.sa_vattr.va_type == VDIR)); + break; + + case SMB_QUERY_FILE_EA_INFO: + case SMB_FILE_EA_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); + break; + + case SMB_QUERY_FILE_NAME_INFO: + case SMB_FILE_NAME_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", sr, + qinfo->qi_namelen, qinfo->qi_name); + break; + + case SMB_QUERY_FILE_ALL_INFO: + case SMB_FILE_ALL_INFORMATION: + /* + * There is a 6-byte pad between Attributes and AllocationSize, + * and a 2-byte pad after the Directory field. + */ + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "TTTTw6.qqlbb2.l", + &qinfo->qi_crtime, + &qinfo->qi_atime, + &qinfo->qi_mtime, + &qinfo->qi_ctime, + dattr, + (uint64_t)allocsz, + (uint64_t)datasz, + qinfo->qi_attr.sa_vattr.va_nlink, + qinfo->qi_delete_on_close, + (qinfo->qi_attr.sa_vattr.va_type == VDIR), + 0); + + (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", + sr, qinfo->qi_namelen, qinfo->qi_name); + break; + + case SMB_QUERY_FILE_ALT_NAME_INFO: + case SMB_FILE_ALT_NAME_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "%lU", sr, + mts_wcequiv_strlen(qinfo->qi_shortname), + qinfo->qi_shortname); + break; + + case SMB_QUERY_FILE_STREAM_INFO: + case SMB_FILE_STREAM_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + smb_encode_stream_info(sr, xa, qinfo); + break; + + case SMB_QUERY_FILE_COMPRESSION_INFO: + case SMB_FILE_COMPRESSION_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "qwbbb3.", + datasz, 0, 0, 0, 0); + break; + + case SMB_FILE_INTERNAL_INFORMATION: + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "q", + qinfo->qi_attr.sa_vattr.va_nodeid); + break; + + case SMB_FILE_ATTR_TAG_INFORMATION: + /* + * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the + * second dword should be the reparse tag. Otherwise + * the tag value should be set to zero. + * We don't support reparse points, so we set the tag + * to zero. + */ + (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); + (void) smb_mbc_encodef(&xa->rep_data_mb, "ll", + (uint32_t)dattr, 0); + break; + + default: + smbsr_error(sr, 0, ERRDOS, ERRunknownlevel); + return (-1); + } + + return (0); +} + +/* + * smb_encode_stream_info + * + * This function encodes the streams information. + * The following rules about how have been derived from observed NT + * behaviour. + * + * If the target is a file: + * 1. If there are no named streams, the response should still contain + * an entry for the unnamed stream. + * 2. If there are named streams, the response should contain an entry + * for the unnamed stream followed by the entries for the named + * streams. + * + * If the target is a directory: + * 1. If there are no streams, the response is complete. Directories + * do not report the unnamed stream. + * 2. If there are streams, the response should contain entries for + * those streams but there should not be an entry for the unnamed + * stream. + * + * Note that the stream name lengths exclude the null terminator but + * the field lengths (i.e. next offset calculations) need to include + * the null terminator and be padded to a multiple of 8 bytes. The + * last entry does not seem to need any padding. + * + * If an error is encountered when trying to read the stream entries + * (smb_odir_read_streaminfo) it is treated as if there are no [more] + * entries. The entries that have been read so far are returned and + * no error is reported. + * + * Offset calculation: + * 2 dwords + 2 quadwords => 4 + 4 + 8 + 8 => 24 + */ +static void +smb_encode_stream_info(smb_request_t *sr, smb_xa_t *xa, smb_queryinfo_t *qinfo) +{ + char *stream_name; + uint32_t next_offset; + uint32_t stream_nlen; + uint32_t pad; + u_offset_t datasz, allocsz; + boolean_t is_dir; + smb_streaminfo_t *sinfo, *sinfo_next; + int rc = 0; + boolean_t done = B_FALSE; + boolean_t eos = B_FALSE; + uint16_t odid; + smb_odir_t *od = NULL; + + smb_node_t *fnode = qinfo->qi_node; + smb_attr_t *attr = &qinfo->qi_attr; + + ASSERT(fnode); + if (SMB_IS_STREAM(fnode)) { + fnode = fnode->n_unode; + ASSERT(fnode); + } + ASSERT(fnode->n_magic == SMB_NODE_MAGIC); + ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING); + + sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); + sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); + is_dir = (attr->sa_vattr.va_type == VDIR); + datasz = attr->sa_vattr.va_size; + allocsz = attr->sa_allocsz; + + odid = smb_odir_openat(sr, fnode); + if (odid != 0) + od = smb_tree_lookup_odir(sr->tid_tree, odid); + if (od != NULL) + rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos); + + if ((od == NULL) || (rc != 0) || (eos)) + done = B_TRUE; + + /* If not a directory, encode an entry for the unnamed stream. */ + if (!is_dir) { + stream_name = "::$DATA"; + stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name); + + if (done) + next_offset = 0; + else + next_offset = 24 + stream_nlen + + smb_ascii_or_unicode_null_len(sr); + + (void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu", sr, + next_offset, stream_nlen, datasz, allocsz, stream_name); + } + + /* + * Since last packet does not have a pad we need to check + * for the next stream before we encode the current one + */ + while (!done) { + stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name); + sinfo_next->si_name[0] = 0; + + rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos); + if ((rc != 0) || (eos)) { + done = B_TRUE; + next_offset = 0; + pad = 0; + } else { + next_offset = 24 + stream_nlen + + smb_ascii_or_unicode_null_len(sr); + pad = smb_pad_align(next_offset, 8); + next_offset += pad; + } + (void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu#.", + sr, next_offset, stream_nlen, + sinfo->si_size, sinfo->si_alloc_size, + sinfo->si_name, pad); + + (void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t)); + } + + kmem_free(sinfo, sizeof (smb_streaminfo_t)); + kmem_free(sinfo_next, sizeof (smb_streaminfo_t)); + if (od) { + smb_odir_close(od); + smb_odir_release(od); + } +} + +/* + * smb_pad_align + * + * Returns the number of bytes required to pad an offset to the + * specified alignment. + */ +uint32_t +smb_pad_align(uint32_t offset, uint32_t align) +{ + uint32_t pad = offset % align; + + if (pad != 0) + pad = align - pad; + + return (pad); +} + +/* + * smb_all_info_filename + * + * This format of filename is only used by the ALL_INFO levels. + * + * Determine the absolute pathname of 'node' within the share. + * For example if the node represents file "test1.txt" in directory + * "dir1" on share "share1", the path would be: \share1\dir1\test1.txt + * + * If node represents a named stream, construct the pathname for the + * associated unnamed stream then append the stream name. + */ +static int +smb_all_info_filename(smb_tree_t *tree, smb_node_t *node, + char *buf, size_t buflen) +{ + char *sharename = tree->t_sharename; + int rc; + size_t len; + vnode_t *vp; + + len = snprintf(buf, buflen, "\\%s", sharename); + if (len == (buflen - 1)) + return (ENAMETOOLONG); + + buf += len; + buflen -= len; + + if (SMB_IS_STREAM(node)) + vp = node->n_unode->vp; + else + vp = node->vp; + + rc = vnodetopath(tree->t_snode->vp, vp, buf, buflen, kcred); + if (rc == 0) { + (void) strsubst(buf, '/', '\\'); + + if (SMB_IS_STREAM(node)) + (void) strlcat(buf, node->od_name, buflen); + } + + return (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) +{ + char *namep = node->od_name; + int rc; + + (void) bzero(qinfo, sizeof (smb_queryinfo_t)); + + if (smb_node_getattr(sr, node, &qinfo->qi_attr) != 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 */ + + /* ALL_INFO levels are a special case for name field */ + if ((infolev == SMB_QUERY_FILE_ALL_INFO) || + (infolev == SMB_FILE_ALL_INFORMATION)) { + rc = smb_all_info_filename(sr->tid_tree, node, + qinfo->qi_name, MAXPATHLEN); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + qinfo->qi_namelen = + smb_ascii_or_unicode_strlen(sr, qinfo->qi_name); + return (0); + } + + /* + * It looks like NT doesn't know what to do with the name "." + * so we convert it to "\\" to indicate the root directory. + * If the leading \ is missing, add it. + */ + if (strcmp(namep, ".") == 0) + (void) strlcpy(qinfo->qi_name, "\\", MAXNAMELEN); + else if (*namep != '\\') + (void) snprintf(qinfo->qi_name, MAXNAMELEN, "\\%s", namep); + else + (void) strlcpy(qinfo->qi_name, namep, MAXNAMELEN); + + qinfo->qi_namelen = smb_ascii_or_unicode_strlen(sr, qinfo->qi_name); + + /* + * For some reason NT will not show the security tab in the root + * directory of a mapped drive unless the filename length is + * greater than one. So we hack the length here to persuade NT + * to show the tab. It should be safe because of the null + * terminator character. + */ + if (qinfo->qi_namelen == 1) + qinfo->qi_namelen = 2; + + /* + * If the shortname is generated by smb_mangle_name() + * it will be returned as the alternative name. + * Otherwise, convert the original name to upper-case + * and return it as the alternative name. + */ + (void) smb_mangle_name(qinfo->qi_attr.sa_vattr.va_nodeid, + namep, qinfo->qi_shortname, qinfo->qi_name83, 0); + if (*qinfo->qi_shortname == 0) { + (void) strlcpy(qinfo->qi_shortname, namep, SMB_SHORTNAMELEN); + (void) utf8_strupr(qinfo->qi_shortname); + } + + return (0); +} + +/* + * smb_query_pipeinfo + * + * Populate smb_queryinfo_t structure for SMB_FTYPE_MESG_PIPE + * (This should become an smb_opipe function.) + */ +static int +smb_query_pipeinfo(smb_request_t *sr, smb_opipe_t *opipe, uint16_t infolev, + smb_queryinfo_t *qinfo) +{ + char *namep = opipe->p_name; + + (void) bzero(qinfo, sizeof (smb_queryinfo_t)); + qinfo->qi_node = NULL; + qinfo->qi_attr.sa_vattr.va_nlink = 1; + qinfo->qi_delete_on_close = 1; + + if ((infolev == SMB_INFO_STANDARD) || + (infolev == SMB_INFO_QUERY_EA_SIZE) || + (infolev == SMB_QUERY_INFORMATION2)) { + qinfo->qi_attr.sa_dosattr = 0; + } else { + qinfo->qi_attr.sa_dosattr = FILE_ATTRIBUTE_NORMAL; + } + + /* If the leading \ is missing from the pipe name, add it. */ + if (*namep != '\\') + (void) snprintf(qinfo->qi_name, MAXNAMELEN, "\\%s", namep); + else + (void) strlcpy(qinfo->qi_name, namep, MAXNAMELEN); + + qinfo->qi_namelen= + smb_ascii_or_unicode_strlen(sr, qinfo->qi_name); + + return (0); +} + +/* + * smb_query_pipe_valid_infolev + * + * If the infolev is not valid for a message pipe, the error + * information is set in sr and B_FALSE is returned. + * Otherwise, returns B_TRUE. + */ +static boolean_t +smb_query_pipe_valid_infolev(smb_request_t *sr, uint16_t infolev) +{ + switch (infolev) { + case SMB_INFO_QUERY_ALL_EAS: + smbsr_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + return (B_FALSE); + + case SMB_QUERY_FILE_ALT_NAME_INFO: + case SMB_FILE_ALT_NAME_INFORMATION: + case SMB_QUERY_FILE_STREAM_INFO: + case SMB_FILE_STREAM_INFORMATION: + case SMB_QUERY_FILE_COMPRESSION_INFO: + case SMB_FILE_COMPRESSION_INFORMATION: + case SMB_FILE_ATTR_TAG_INFORMATION: + smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + return (B_FALSE); + } + + return (B_TRUE); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_information.c b/usr/src/uts/common/fs/smbsrv/smb_query_information.c deleted file mode 100644 index 8718361d06..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_query_information.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: query_information - * - * This request is sent to obtain information about a file. - * - * Client Request Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 0 - * USHORT ByteCount; Count of data bytes; min = 2 - * UCHAR BufferFormat; 0x04 - * STRING FileName[]; File name - * - * FileName is the fully qualified name of the file relative to the Tid in - * the header. - * - * Server Response Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 10 - * USHORT FileAttributes; - * UTIME LastWriteTime; Time of last write - * ULONG FileSize; File size - * USHORT Reserved [5]; Reserved - client should ignore - * USHORT ByteCount; Count of data bytes = 0 - * - * FileAttributes are as described in the "Attributes Encoding" section of - * this document. - * - * Note that FileSize is limited to 32 bits, this request is inappropriate - * for files whose size is too large. - * - * NOTES: - * Some clients send a NULL file name. Right now we return ERRbadfile - * until we find out what a MS client would send... - */ - -#include <smbsrv/smb_incl.h> -#include <smbsrv/smb_fsops.h> - -smb_sdrc_t -smb_pre_query_information(smb_request_t *sr) -{ - smb_fqi_t *fqi = &sr->arg.dirop.fqi; - int rc; - - rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path); - if (rc == 0) { - if (strlen(fqi->fq_path.pn_path) == 0) - fqi->fq_path.pn_path = "\\"; - } - - DTRACE_SMB_2(op__QueryInformation__start, smb_request_t *, sr, - smb_fqi_t *, fqi); - - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} - -void -smb_post_query_information(smb_request_t *sr) -{ - DTRACE_SMB_1(op__QueryInformation__done, smb_request_t *, sr); -} - -smb_sdrc_t -smb_com_query_information(smb_request_t *sr) -{ - char *path = sr->arg.dirop.fqi.fq_path.pn_path; - char *name = sr->arg.dirop.fqi.fq_last_comp; - int rc; - uint16_t dattr; - uint32_t write_time; - u_offset_t datasz; - smb_node_t *dir_node; - smb_node_t *node; - smb_attr_t attr; - timestruc_t *mtime; - - if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, - ERROR_ACCESS_DENIED); - return (SDRC_ERROR); - } - - if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, - sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name)) - != 0) { - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - if ((rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, dir_node, name, &node)) != 0) { - smb_node_release(dir_node); - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - smb_node_release(dir_node); - rc = smb_node_getattr(sr, node, &attr); - smb_node_release(node); - - if (rc != 0) { - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - dattr = attr.sa_dosattr & FILE_ATTRIBUTE_MASK; - mtime = &attr.sa_vattr.va_mtime; - write_time = smb_gmt2local(sr, mtime->tv_sec); - datasz = attr.sa_vattr.va_size; - if (datasz > UINT_MAX) - datasz = UINT_MAX; - - rc = smbsr_encode_result(sr, 10, 0, "bwll10.w", - 10, dattr, write_time, (uint32_t)datasz, 0); - - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_information2.c b/usr/src/uts/common/fs/smbsrv/smb_query_information2.c deleted file mode 100644 index 0c03e480fc..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_query_information2.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: query_information2 - * - * This SMB is gets information about the file represented by Fid. - * - * Client Request Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 2 - * USHORT Fid; File handle - * USHORT ByteCount; Count of data bytes = 0 - * - * Server Response Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 11 - * SMB_DATE CreationDate; - * SMB_TIME CreationTime; - * SMB_DATE LastAccessDate; - * SMB_TIME LastAccessTime; - * SMB_DATE LastWriteDate; - * SMB_TIME LastWriteTime; - * ULONG FileDataSize; File end of data - * ULONG FileAllocationSize; File allocation size - * USHORT FileAttributes; - * USHORT ByteCount; Count of data bytes; min = 0 - * - * The file being interrogated is specified by Fid, which must possess at - * least read permission. - * - * FileAttributes are described in the "File Attribute Encoding" section - * elsewhere in this document. - */ - -#include <smbsrv/smb_incl.h> - -smb_sdrc_t -smb_pre_query_information2(smb_request_t *sr) -{ - int rc; - - rc = smbsr_decode_vwv(sr, "w", &sr->smb_fid); - - DTRACE_SMB_1(op__QueryInformation2__start, smb_request_t *, sr); - - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} - -void -smb_post_query_information2(smb_request_t *sr) -{ - DTRACE_SMB_1(op__QueryInformation2__done, smb_request_t *, sr); -} - -smb_sdrc_t -smb_com_query_information2(smb_request_t *sr) -{ - smb_node_t *node; - smb_attr_t attr; - u_offset_t size64; - uint32_t datasz, allocsz; - int rc; - - smbsr_lookup_file(sr); - if (sr->fid_ofile == NULL) { - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); - return (SDRC_ERROR); - } - - if (sr->fid_ofile->f_ftype != SMB_FTYPE_DISK) { - rc = smbsr_encode_result(sr, 11, 0, "blllllww", - 11, 0, 0, 0, 0, 0, 0, 0); - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); - } - - 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); - } - - size64 = attr.sa_vattr.va_size; - datasz = (size64 > UINT_MAX) ? UINT_MAX : (uint32_t)size64; - - size64 = attr.sa_vattr.va_nblocks * DEV_BSIZE; - allocsz = (size64 > UINT_MAX) ? UINT_MAX : (uint32_t)size64; - - rc = smbsr_encode_result(sr, 11, 0, "byyyllww", - 11, /* wct */ - smb_gmt2local(sr, attr.sa_crtime.tv_sec), - /* LastAccessTime */ - smb_gmt2local(sr, attr.sa_vattr.va_atime.tv_sec), - /* LastWriteTime */ - smb_gmt2local(sr, attr.sa_vattr.va_mtime.tv_sec), - datasz, - allocsz, - attr.sa_dosattr & FILE_ATTRIBUTE_MASK, - 0); /* bcc */ - - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_rename.c b/usr/src/uts/common/fs/smbsrv/smb_rename.c index 18365da04e..6da6809374 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_rename.c +++ b/usr/src/uts/common/fs/smbsrv/smb_rename.c @@ -130,19 +130,36 @@ smb_com_rename(smb_request_t *sr) static int smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) { - smb_node_t *src_node; + smb_node_t *src_node, *tnode; char *dstname; DWORD status; int rc; int count; + char *path; - if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) + tnode = sr->tid_tree->t_snode; + + /* Lookup the source node. It MUST exist. */ + path = src_fqi->fq_path.pn_path; + rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, + &src_fqi->fq_dnode, src_fqi->fq_last_comp); + if (rc != 0) return (rc); - src_node = src_fqi->fq_fnode; + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, + src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); + if (rc != 0) { + smb_node_release(src_fqi->fq_dnode); + return (rc); + } - if ((rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr)) != 0) - goto rename_cleanup_nodes; + src_node = src_fqi->fq_fnode; + rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr); + if (rc != 0) { + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (rc); + } /* * Break the oplock before access checks. If a client @@ -167,88 +184,100 @@ smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) if (status == NT_STATUS_SHARING_VIOLATION) { smb_node_end_crit(src_node); - rc = EPIPE; /* = ERRbadshare */ - goto rename_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (EPIPE); /* = ERRbadshare */ } status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); if (status != NT_STATUS_SUCCESS) { smb_node_end_crit(src_node); - rc = EACCES; - goto rename_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (EACCES); + } + + /* Lookup destination node. */ + path = dst_fqi->fq_path.pn_path; + rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, + &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); + if (rc != 0) { + smb_node_end_crit(src_node); + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (rc); + } + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, + dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); + if ((rc != 0) && (rc != ENOENT)) { + smb_node_end_crit(src_node); + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + smb_node_release(dst_fqi->fq_dnode); + return (rc); } if (utf8_strcasecmp(src_fqi->fq_path.pn_path, dst_fqi->fq_path.pn_path) == 0) { - if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { - smb_node_end_crit(src_node); - goto rename_cleanup_nodes; - } - /* - * Because the fqm parameter to smbd_fs_query() was 0, - * dst_fqi->fq_fnode may be NULL. - */ if (dst_fqi->fq_fnode) smb_node_release(dst_fqi->fq_fnode); - rc = strcmp(src_fqi->fq_od_name, dst_fqi->fq_last_comp); + rc = strcmp(src_fqi->fq_fnode->od_name, dst_fqi->fq_last_comp); if (rc == 0) { smb_node_end_crit(src_node); - goto rename_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + smb_node_release(dst_fqi->fq_dnode); + return (0); } rc = smb_fsop_rename(sr, sr->user_cr, - src_fqi->fq_dnode, src_fqi->fq_od_name, + src_fqi->fq_dnode, src_fqi->fq_fnode->od_name, dst_fqi->fq_dnode, dst_fqi->fq_last_comp); smb_node_end_crit(src_node); if (rc == 0) smb_node_notify_change(dst_fqi->fq_dnode); - goto rename_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + smb_node_release(dst_fqi->fq_dnode); + return (rc); } - rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); - if (rc != 0) { + /* dst node must not exist */ + if (dst_fqi->fq_fnode) { smb_node_end_crit(src_node); - goto rename_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + smb_node_release(dst_fqi->fq_fnode); + smb_node_release(dst_fqi->fq_dnode); + return (EEXIST); } /* - * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode - * is valid (dst_fqi->fq_fnode is NULL). - */ - - /* * If the source name is mangled but the source and destination * on-disk names are identical, we'll use the on-disk name. */ if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) && (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) { - dstname = src_fqi->fq_od_name; + dstname = src_fqi->fq_fnode->od_name; } else { dstname = dst_fqi->fq_last_comp; } rc = smb_fsop_rename(sr, sr->user_cr, - src_fqi->fq_dnode, src_fqi->fq_od_name, + src_fqi->fq_dnode, src_fqi->fq_fnode->od_name, dst_fqi->fq_dnode, dstname); smb_node_end_crit(src_node); - if (rc == 0) smb_node_notify_change(dst_fqi->fq_dnode); - -rename_cleanup_nodes: - smb_node_release(src_node); + smb_node_release(src_fqi->fq_fnode); smb_node_release(src_fqi->fq_dnode); - - if (dst_fqi->fq_dnode) - smb_node_release(dst_fqi->fq_dnode); - - SMB_NULL_FQI_NODES(*src_fqi); - SMB_NULL_FQI_NODES(*dst_fqi); + smb_node_release(dst_fqi->fq_dnode); return (rc); } @@ -377,18 +406,35 @@ smb_com_nt_rename(smb_request_t *sr) static int smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) { - smb_node_t *src_fnode; + smb_node_t *src_fnode, *tnode; DWORD status; int rc; int count; + char *path; - if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) + tnode = sr->tid_tree->t_snode; + + /* Lookup the source node. It MUST exist. */ + path = src_fqi->fq_path.pn_path; + rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, + &src_fqi->fq_dnode, src_fqi->fq_last_comp); + if (rc != 0) return (rc); - src_fnode = src_fqi->fq_fnode; + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, + src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); + if (rc != 0) { + smb_node_release(src_fqi->fq_dnode); + return (rc); + } - if ((rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr)) != 0) - goto link_cleanup_nodes; + src_fnode = src_fqi->fq_fnode; + rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr); + if (rc != 0) { + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (rc); + } /* * Break the oplock before access checks. If a client @@ -412,52 +458,61 @@ smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) if (status == NT_STATUS_SHARING_VIOLATION) { smb_node_end_crit(src_fnode); - rc = EPIPE; /* = ERRbadshare */ - goto link_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (EPIPE); /* = ERRbadshare */ } status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE); - if (status != NT_STATUS_SUCCESS) { smb_node_end_crit(src_fnode); - rc = EACCES; - goto link_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (EACCES); } if (utf8_strcasecmp(src_fqi->fq_path.pn_path, dst_fqi->fq_path.pn_path) == 0) { smb_node_end_crit(src_fnode); - rc = 0; - goto link_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (0); } - rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); + /* Lookup the destination node. It MUST NOT exist. */ + path = dst_fqi->fq_path.pn_path; + rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, + &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); if (rc != 0) { smb_node_end_crit(src_fnode); - goto link_cleanup_nodes; + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + return (rc); + } + + rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, + dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); + if (rc == 0) { + smb_node_release(dst_fqi->fq_fnode); + rc = EEXIST; + } + if (rc != ENOENT) { + smb_node_end_crit(src_fnode); + smb_node_release(src_fqi->fq_fnode); + smb_node_release(src_fqi->fq_dnode); + smb_node_release(dst_fqi->fq_dnode); + return (rc); } - /* - * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode - * is valid (dst_fqi->fq_fnode is NULL). - */ rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode, dst_fqi->fq_last_comp); smb_node_end_crit(src_fnode); - if (rc == 0) smb_node_notify_change(dst_fqi->fq_dnode); - -link_cleanup_nodes: - smb_node_release(src_fnode); + smb_node_release(src_fqi->fq_fnode); smb_node_release(src_fqi->fq_dnode); - - if (dst_fqi->fq_dnode) - smb_node_release(dst_fqi->fq_dnode); - - SMB_NULL_FQI_NODES(*src_fqi); - SMB_NULL_FQI_NODES(*dst_fqi); + smb_node_release(dst_fqi->fq_dnode); return (rc); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c new file mode 100644 index 0000000000..2f9111516d --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c @@ -0,0 +1,683 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Trans2 Set File/Path Information Levels: + * + * SMB_INFO_STANDARD + * SMB_INFO_SET_EAS + * SMB_SET_FILE_BASIC_INFO + * SMB_SET_FILE_DISPOSITION_INFO + * SMB_SET_FILE_END_OF_FILE_INFO + * SMB_SET_FILE_ALLOCATION_INFO + * + * Supported Passthrough levels: + * SMB_FILE_BASIC_INFORMATION + * SMB_FILE_DISPOSITION_INFORMATION + * SMB_FILE_END_OF_FILE_INFORMATION + * SMB_FILE_ALLOCATION_INFORMATION + * + * Internal levels representing non trans2 requests + * SMB_SET_INFORMATION + * SMB_SET_INFORMATION2 + */ + +/* + * Setting timestamps: + * The behaviour when the time field is set to -1 is not documented + * but is generally treated like 0, meaning that that server file + * system assigned value need not be changed. + * + * Setting attributes - FILE_ATTRIBUTE_NORMAL: + * SMB_SET_INFORMATION - + * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set + * do NOT change the file's attributes. + * SMB_SET_BASIC_INFO - + * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set + * clear (0) the file's attributes. + * - if the specified attributes are 0 do NOT change the file's + * attributes. + */ + +#include <smbsrv/smb_incl.h> +#include <smbsrv/smb_fsops.h> + +typedef struct smb_setinfo { + uint16_t si_infolev; + smb_xa_t *si_xa; + smb_node_t *si_node; +} smb_setinfo_t; + +/* + * These functions all return 0 (success) or -1 (error). + * They set error details in the sr when appropriate. + */ +static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t); +static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t, char *); +static int smb_set_fileinfo(smb_request_t *, smb_setinfo_t *); +static int smb_set_information(smb_request_t *, smb_setinfo_t *); +static int smb_set_information2(smb_request_t *, smb_setinfo_t *); +static int smb_set_standard_info(smb_request_t *, smb_setinfo_t *); +static int smb_set_basic_info(smb_request_t *, smb_setinfo_t *); +static int smb_set_disposition_info(smb_request_t *, smb_setinfo_t *); +static int smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *); +static int smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *); + +/* + * smb_com_trans2_set_file_information + */ +smb_sdrc_t +smb_com_trans2_set_file_information(smb_request_t *sr, smb_xa_t *xa) +{ + uint16_t infolev; + + if (smb_mbc_decodef(&xa->req_param_mb, "ww", + &sr->smb_fid, &infolev) != 0) + return (SDRC_ERROR); + + if (smb_set_by_fid(sr, xa, infolev) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * smb_com_trans2_set_path_information + */ +smb_sdrc_t +smb_com_trans2_set_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_set_by_path(sr, xa, infolev, path) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * smb_com_set_information (aka setattr) + */ +smb_sdrc_t +smb_pre_set_information(smb_request_t *sr) +{ + DTRACE_SMB_1(op__SetInformation__start, smb_request_t *, sr); + return (SDRC_SUCCESS); +} + +void +smb_post_set_information(smb_request_t *sr) +{ + DTRACE_SMB_1(op__SetInformation__done, smb_request_t *, sr); +} + +smb_sdrc_t +smb_com_set_information(smb_request_t *sr) +{ + uint16_t infolev = SMB_SET_INFORMATION; + char *path; + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { + smbsr_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + return (SDRC_ERROR); + } + + if (smbsr_decode_data(sr, "%S", sr, &path) != 0) + return (SDRC_ERROR); + + if (smb_set_by_path(sr, NULL, infolev, path) != 0) + return (SDRC_ERROR); + + if (smbsr_encode_empty_result(sr) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * smb_com_set_information2 (aka setattre) + */ +smb_sdrc_t +smb_pre_set_information2(smb_request_t *sr) +{ + DTRACE_SMB_1(op__SetInformation2__start, smb_request_t *, sr); + return (SDRC_SUCCESS); +} + +void +smb_post_set_information2(smb_request_t *sr) +{ + DTRACE_SMB_1(op__SetInformation2__done, smb_request_t *, sr); +} + +smb_sdrc_t +smb_com_set_information2(smb_request_t *sr) +{ + uint16_t infolev = SMB_SET_INFORMATION2; + + if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0) + return (SDRC_ERROR); + + if (smb_set_by_fid(sr, NULL, infolev) != 0) + return (SDRC_ERROR); + + if (smbsr_encode_empty_result(sr) != 0) + return (SDRC_ERROR); + + return (SDRC_SUCCESS); +} + +/* + * smb_set_by_fid + * + * Common code for setting file information by open file id. + * Use the id to identify the node object and invoke smb_set_fileinfo + * for that node. + * + * Setting attributes on a named pipe by id is handled by simply + * returning success. + */ +static int +smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev) +{ + int rc; + smb_setinfo_t sinfo; + + if (SMB_TREE_IS_READONLY(sr)) { + smbsr_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + return (-1); + } + + if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) + return (0); + + smbsr_lookup_file(sr); + if (sr->fid_ofile == NULL) { + smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); + return (-1); + } + + if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { + smbsr_release_file(sr); + return (0); + } + + sr->user_cr = smb_ofile_getcred(sr->fid_ofile); + + sinfo.si_xa = xa; + sinfo.si_infolev = infolev; + sinfo.si_node = sr->fid_ofile->f_node; + rc = smb_set_fileinfo(sr, &sinfo); + + smbsr_release_file(sr); + return (rc); +} + +/* + * smb_set_by_path + * + * Common code for setting file information by file name. + * Use the file name to identify the node object and invoke + * smb_set_fileinfo for that node. + * + * Setting attributes on a named pipe by name is an error and + * is handled in the calling functions so that they can return + * the appropriate error status code (which differs by caller). + */ +static int +smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, + uint16_t infolev, char *path) +{ + int rc; + smb_setinfo_t sinfo; + smb_node_t *node, *dnode; + char *name; + + if (SMB_TREE_IS_READONLY(sr)) { + smbsr_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + return (-1); + } + + name = kmem_alloc(MAXNAMELEN, KM_SLEEP); + rc = smb_pathname_reduce(sr, sr->user_cr, path, + sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode, name); + if (rc == 0) { + rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, + sr->tid_tree->t_snode, dnode, name, &node); + smb_node_release(dnode); + } + kmem_free(name, MAXNAMELEN); + + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + /* Break any conflicting oplock for subsequent attribute setting */ + if (smb_oplock_conflict(node, sr->session, NULL)) { + (void) smb_oplock_break(node, sr->session, B_FALSE); + } + + sinfo.si_xa = xa; + sinfo.si_infolev = infolev; + sinfo.si_node = node; + rc = smb_set_fileinfo(sr, &sinfo); + + smb_node_release(node); + return (rc); +} + +/* + * smb_set_fileinfo + */ +static int +smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + switch (sinfo->si_infolev) { + case SMB_SET_INFORMATION: + return (smb_set_information(sr, sinfo)); + + case SMB_SET_INFORMATION2: + return (smb_set_information2(sr, sinfo)); + + case SMB_INFO_STANDARD: + return (smb_set_standard_info(sr, sinfo)); + + case SMB_INFO_SET_EAS: + /* EAs not supported */ + return (0); + + case SMB_SET_FILE_BASIC_INFO: + case SMB_FILE_BASIC_INFORMATION: + return (smb_set_basic_info(sr, sinfo)); + + case SMB_SET_FILE_DISPOSITION_INFO: + case SMB_FILE_DISPOSITION_INFORMATION: + return (smb_set_disposition_info(sr, sinfo)); + + case SMB_SET_FILE_END_OF_FILE_INFO: + case SMB_FILE_END_OF_FILE_INFORMATION: + return (smb_set_eof_info(sr, sinfo)); + + case SMB_SET_FILE_ALLOCATION_INFO: + case SMB_FILE_ALLOCATION_INFORMATION: + return (smb_set_alloc_info(sr, sinfo)); + + default: + break; + } + + smbsr_error(sr, NT_STATUS_INVALID_INFO_CLASS, + ERRDOS, ERROR_INVALID_PARAMETER); + return (-1); +} + +/* + * smb_set_information + * + * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the + * target is not a directory. + * + * For compatibility with Windows Servers, if the specified + * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change + * the file's attributes. + */ +static int +smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + int rc; + uint16_t attributes; + smb_node_t *node = sinfo->si_node; + smb_attr_t attr; + uint32_t mtime; + + if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0) + return (-1); + + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) && + (!smb_node_is_dir(node))) { + smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + return (-1); + } + + bzero(&attr, sizeof (smb_attr_t)); + if (attributes != FILE_ATTRIBUTE_NORMAL) { + attr.sa_dosattr = attributes; + attr.sa_mask |= SMB_AT_DOSATTR; + } + + if (mtime != 0 && mtime != UINT_MAX) { + attr.sa_vattr.va_mtime.tv_sec = + smb_time_local_to_gmt(sr, mtime); + attr.sa_mask |= SMB_AT_MTIME; + } + + rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_information2 + */ +static int +smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + int rc; + uint32_t crtime, atime, mtime; + smb_attr_t attr; + + if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0) + return (-1); + + bzero(&attr, sizeof (smb_attr_t)); + if (mtime != 0 && mtime != UINT_MAX) { + attr.sa_vattr.va_mtime.tv_sec = + smb_time_local_to_gmt(sr, mtime); + attr.sa_mask |= SMB_AT_MTIME; + } + + if (crtime != 0 && crtime != UINT_MAX) { + attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime); + attr.sa_mask |= SMB_AT_CRTIME; + } + + if (atime != 0 && atime != UINT_MAX) { + attr.sa_vattr.va_atime.tv_sec = + smb_time_local_to_gmt(sr, atime); + attr.sa_mask |= SMB_AT_ATIME; + } + + rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr, + sr->fid_ofile, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_standard_info + * + * Sets standard file/path information. + */ +static int +smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + smb_attr_t attr; + uint32_t crtime, atime, mtime; + smb_node_t *node = sinfo->si_node; + int rc; + + if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "yyy", + &crtime, &atime, &mtime) != 0) { + return (-1); + } + + bzero(&attr, sizeof (smb_attr_t)); + if (mtime != 0 && mtime != (uint32_t)-1) { + attr.sa_vattr.va_mtime.tv_sec = + smb_time_local_to_gmt(sr, mtime); + attr.sa_mask |= SMB_AT_MTIME; + } + + if (crtime != 0 && crtime != (uint32_t)-1) { + attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime); + attr.sa_mask |= SMB_AT_CRTIME; + } + + if (atime != 0 && atime != (uint32_t)-1) { + attr.sa_vattr.va_atime.tv_sec = + smb_time_local_to_gmt(sr, atime); + attr.sa_mask |= SMB_AT_ATIME; + } + + rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_basic_info + * + * Sets basic file/path information. + * + * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the + * target is not a directory. + * + * For compatibility with windows servers: + * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set + * clear (0) the file's attributes. + * - if the specified attributes are 0 do NOT change the file's attributes. + */ +static int +smb_set_basic_info(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + int rc; + uint64_t crtime, atime, mtime, ctime; + uint16_t attributes; + smb_attr_t attr; + smb_node_t *node = sinfo->si_node; + + if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "qqqqw", + &crtime, &atime, &mtime, &ctime, &attributes) != 0) { + return (-1); + } + + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) && + (!smb_node_is_dir(node))) { + smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + return (-1); + } + + bzero(&attr, sizeof (smb_attr_t)); + if (ctime != 0 && ctime != (uint64_t)-1) { + smb_time_nt_to_unix(ctime, &attr.sa_vattr.va_ctime); + attr.sa_mask |= SMB_AT_CTIME; + } + + if (crtime != 0 && crtime != (uint64_t)-1) { + smb_time_nt_to_unix(crtime, &attr.sa_crtime); + attr.sa_mask |= SMB_AT_CRTIME; + } + + if (mtime != 0 && mtime != (uint64_t)-1) { + smb_time_nt_to_unix(mtime, &attr.sa_vattr.va_mtime); + attr.sa_mask |= SMB_AT_MTIME; + } + + if (atime != 0 && atime != (uint64_t)-1) { + smb_time_nt_to_unix(atime, &attr.sa_vattr.va_atime); + attr.sa_mask |= SMB_AT_ATIME; + } + + if (attributes != 0) { + attr.sa_dosattr = attributes; + attr.sa_mask |= SMB_AT_DOSATTR; + } + + rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_eof_info + */ +static int +smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + int rc; + smb_attr_t attr; + uint64_t eof; + smb_node_t *node = sinfo->si_node; + + if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "q", &eof) != 0) + return (-1); + + if (smb_node_is_dir(node)) { + smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + return (-1); + } + + bzero(&attr, sizeof (smb_attr_t)); + attr.sa_mask = SMB_AT_SIZE; + attr.sa_vattr.va_size = (u_offset_t)eof; + rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_alloc_info + */ +static int +smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + int rc; + smb_attr_t attr; + uint64_t allocsz; + smb_node_t *node = sinfo->si_node; + + if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "q", &allocsz) != 0) + return (-1); + + if (smb_node_is_dir(node)) { + smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, + ERRDOS, ERROR_INVALID_PARAMETER); + return (-1); + } + + bzero(&attr, sizeof (smb_attr_t)); + attr.sa_mask = SMB_AT_ALLOCSZ; + attr.sa_allocsz = (u_offset_t)allocsz; + rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); + if (rc != 0) { + smbsr_errno(sr, rc); + return (-1); + } + + return (0); +} + +/* + * smb_set_disposition_info + * + * Set/Clear DELETE_ON_CLOSE flag for an open file. + * File should have been opened with DELETE access otherwise + * the operation is not permitted. + * + * NOTE: The node should be marked delete-on-close upon the receipt + * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set. + * It is different than both SmbNtCreateAndX and SmbNtTransact, which + * set delete-on-close on the ofile and defer setting the flag on the + * node until the file is closed. + * + * Observation of Windows 2000 indicates the following: + * + * 1) If a file is not opened with delete-on-close create options and + * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo) + * using that open file handle, any subsequent open requests will fail + * with DELETE_PENDING. + * + * 2) If a file is opened with delete-on-close create options and the + * client attempts to unset delete-on-close via Trans2SetFileInfo + * (SetDispositionInfo) prior to the file close, any subsequent open + * requests will still fail with DELETE_PENDING after the file is closed. + * + * 3) If a file is opened with delete-on-close create options and that + * file handle (not the last open handle and the only file handle + * with delete-on-close set) is closed. Any subsequent open requests + * will fail with DELETE_PENDING. Unsetting delete-on-close via + * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the + * node delete-on-close flag, which will result in the file not being + * removed even after the last file handle is closed. + */ +static int +smb_set_disposition_info(smb_request_t *sr, smb_setinfo_t *sinfo) +{ + unsigned char mark_delete; + uint32_t flags = 0; + + if (smb_mbc_decodef(&sinfo->si_xa->req_data_mb, "b", &mark_delete) != 0) + return (-1); + + if ((sr->fid_ofile == NULL) || + !(smb_ofile_granted_access(sr->fid_ofile) & DELETE)) { + smbsr_error(sr, NT_STATUS_ACCESS_DENIED, + ERRDOS, ERROR_ACCESS_DENIED); + return (-1); + } + + if (mark_delete) { + if (SMB_TREE_SUPPORTS_CATIA(sr)) + flags |= SMB_CATIA; + + if (smb_node_set_delete_on_close(sinfo->si_node, + sr->user_cr, flags)) { + smbsr_error(sr, NT_STATUS_CANNOT_DELETE, + ERRDOS, ERROR_ACCESS_DENIED); + return (-1); + } + } else { + smb_node_reset_delete_on_close(sinfo->si_node); + } + return (0); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_information.c b/usr/src/uts/common/fs/smbsrv/smb_set_information.c deleted file mode 100644 index e4073921b2..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_set_information.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: set_information - * - * This message is sent to change the information about a file. - * - * Client Request Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 8 - * USHORT FileAttributes; Attributes of the file - * UTIME LastWriteTime; Time of last write - * USHORT Reserved [5]; Reserved (must be 0) - * USHORT ByteCount; Count of data bytes; min = 2 - * UCHAR BufferFormat; 0x04 - * STRING FileName[]; File name - * - * FileName is the fully qualified name of the file relative to the Tid. - * - * Support of all parameters is optional. A server which does not - * implement one of the parameters will ignore that field. If the - * LastWriteTime field contain zero then the file's time is not changed. - * - * Server Response Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 0 - * USHORT ByteCount; Count of data bytes = 0 - * - */ - -#include <smbsrv/smb_incl.h> -#include <smbsrv/smb_fsops.h> - -smb_sdrc_t -smb_pre_set_information(smb_request_t *sr) -{ - DTRACE_SMB_1(op__SetInformation__start, smb_request_t *, sr); - return (SDRC_SUCCESS); -} - -void -smb_post_set_information(smb_request_t *sr) -{ - DTRACE_SMB_1(op__SetInformation__done, smb_request_t *, sr); -} - -smb_sdrc_t -smb_com_set_information(smb_request_t *sr) -{ - int rc; - unsigned short dattr; - char *path; - smb_node_t *dir_node; - smb_node_t *node; - smb_attr_t attr; - char *name; - uint32_t last_wtime; - - if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { - (void) smbsr_encode_empty_result(sr); - return (SDRC_SUCCESS); - } - - name = kmem_alloc(MAXNAMELEN, KM_SLEEP); - if (smbsr_decode_vwv(sr, "wl10.", &dattr, &last_wtime) != 0) { - kmem_free(name, MAXNAMELEN); - return (SDRC_ERROR); - } - - if (smbsr_decode_data(sr, "%S", sr, &path) != 0) { - kmem_free(name, MAXNAMELEN); - return (SDRC_ERROR); - } - - rc = smb_pathname_reduce(sr, sr->user_cr, path, - sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name); - if (rc != 0) { - kmem_free(name, MAXNAMELEN); - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, dir_node, name, &node); - if (rc != 0) { - smb_node_release(dir_node); - kmem_free(name, MAXNAMELEN); - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - smb_node_release(dir_node); - - /* - * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the - * target is not a directory. - */ - if ((dattr & FILE_ATTRIBUTE_DIRECTORY) && - (!smb_node_is_dir(node))) { - smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, - ERRDOS, ERROR_INVALID_PARAMETER); - return (SDRC_ERROR); - } - - if (smb_oplock_conflict(node, sr->session, NULL)) { - /* - * for the benefit of attribute setting later on - */ - (void) smb_oplock_break(node, sr->session, B_FALSE); - } - - bzero(&attr, sizeof (smb_attr_t)); - - /* - * For compatibility with Windows Servers, if the specified - * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change - * the file's attributes. - * Note - this is different from TRANS2_SET_PATH/FILE_INFORMATION - */ - if (dattr != FILE_ATTRIBUTE_NORMAL) { - attr.sa_dosattr = dattr; - attr.sa_mask |= SMB_AT_DOSATTR; - } - - /* - * The behaviour when the time field is set to -1 - * is not documented but is generally treated like 0, - * meaning that that server file system assigned value - * need not be changed. - */ - if (last_wtime != 0 && last_wtime != 0xFFFFFFFF) { - attr.sa_vattr.va_mtime.tv_sec = smb_local2gmt(sr, last_wtime); - attr.sa_mask |= SMB_AT_MTIME; - } - - rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr); - smb_node_release(node); - kmem_free(name, MAXNAMELEN); - - if (rc) { - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - rc = smbsr_encode_empty_result(sr); - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_set_information2.c b/usr/src/uts/common/fs/smbsrv/smb_set_information2.c deleted file mode 100644 index c3a5257598..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_set_information2.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: set_information2 - * - * Client Request Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 7 - * USHORT Fid; File handle - * SMB_DATE CreationDate; - * SMB_TIME CreationTime; - * SMB_DATE LastAccessDate; - * SMB_TIME LastAccessTime; - * SMB_DATE LastWriteDate; - * SMB_TIME LastWriteTime; - * USHORT ByteCount; Count of data bytes = 0 - * - * SMB_COM_SET_INFORMATION2 sets information about the file represented by - * Fid. The target file is updated from the values specified. A date or - * time value or zero indicates to leave that specific date and time - * unchanged. - * - * Server Response Description - * ================================== ================================= - * - * UCHAR WordCount; Count of parameter words = 0 - * USHORT ByteCount; Count of data bytes = 0 - * - * Fid must be open with (at least) write permission. - */ - -#include <smbsrv/smb_incl.h> - -smb_sdrc_t -smb_pre_set_information2(smb_request_t *sr) -{ - DTRACE_SMB_1(op__SetInformation2__start, smb_request_t *, sr); - return (SDRC_SUCCESS); -} - -void -smb_post_set_information2(smb_request_t *sr) -{ - DTRACE_SMB_1(op__SetInformation2__done, smb_request_t *, sr); -} - -smb_sdrc_t -smb_com_set_information2(smb_request_t *sr) -{ - uint32_t creation, last_access, last_write; /* times */ - smb_attr_t attr; - smb_node_t *node; - int rc; - - rc = smbsr_decode_vwv(sr, "wyyy", &sr->smb_fid, - &creation, &last_access, &last_write); - if (rc != 0) - return (SDRC_ERROR); - - smbsr_lookup_file(sr); - if (sr->fid_ofile == NULL) { - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); - return (SDRC_ERROR); - } - - sr->user_cr = smb_ofile_getcred(sr->fid_ofile); - - node = sr->fid_ofile->f_node; - - if (node == 0 || sr->fid_ofile->f_ftype != SMB_FTYPE_DISK) { - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); - return (SDRC_ERROR); - } - - bzero(&attr, sizeof (smb_attr_t)); - - /* - * The behaviour when the time field is set to -1 - * is not documented but is generally treated like 0, - * meaning that that server file system assigned value - * need not be changed. - */ - if (last_write != 0 && last_write != UINT_MAX) { - attr.sa_vattr.va_mtime.tv_sec = smb_local2gmt(sr, last_write); - attr.sa_mask |= SMB_AT_MTIME; - } - - if (creation != 0 && creation != UINT_MAX) { - attr.sa_crtime.tv_sec = smb_local2gmt(sr, creation); - attr.sa_mask |= SMB_AT_CRTIME; - } - - if (last_access != 0 && last_access != UINT_MAX) { - attr.sa_vattr.va_atime.tv_sec = smb_local2gmt(sr, last_access); - attr.sa_mask |= SMB_AT_ATIME; - } - - rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); - if (rc) { - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - rc = smbsr_encode_empty_result(sr); - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c index d2b618fd5c..f3aef8da99 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c +++ b/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c @@ -664,9 +664,9 @@ smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa, fileinfo->fi_cookie); (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwbu", sr, - smb_gmt2local(sr, fileinfo->fi_crtime.tv_sec), - smb_gmt2local(sr, fileinfo->fi_atime.tv_sec), - smb_gmt2local(sr, fileinfo->fi_mtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, @@ -697,9 +697,9 @@ smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa, tmpbuf[namelen] = '\0'; (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb#c", sr, - smb_gmt2local(sr, fileinfo->fi_crtime.tv_sec), - smb_gmt2local(sr, fileinfo->fi_atime.tv_sec), - smb_gmt2local(sr, fileinfo->fi_mtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec), + smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec), dsize32, asize32, fileinfo->fi_dosattr, diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c deleted file mode 100644 index 1141fb6d12..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_file_info.c +++ /dev/null @@ -1,636 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: trans2_query_file_information - * - * This request is used to get information about a specific file or - * subdirectory given a handle to it. - * - * Client Request Value - * ========================== ========================================== - * - * WordCount 15 - * MaxSetupCount 0 - * SetupCount 1 - * Setup[0] TRANS2_QUERY_FILE_INFORMATION - * - * Parameter Block Encoding Description - * ========================== ========================================== - * - * USHORT Fid; Handle of file for request - * USHORT InformationLevel; Level of information requested - * - * The available information levels, as well as the format of the response - * are identical to TRANS2_QUERY_PATH_INFORMATION. - */ - -#include <smbsrv/smb_vops.h> -#include <smbsrv/smb_incl.h> -#include <smbsrv/smb_fsops.h> - -uint32_t smb_pad_align(uint32_t offset, uint32_t align); -void smb_encode_smb_datetimes(smb_request_t *, smb_xa_t *, smb_attr_t *); -void smb_encode_nt_times(smb_request_t *, smb_xa_t *, smb_attr_t *); -extern int smb_query_all_info_filename(smb_tree_t *, smb_node_t *, - char *, size_t); - - -/* - * smb_com_trans2_query_file_information - * - * Observation of Windows 2000 indicates the following: - * - * 1) If a file is opened with delete-on-close create options, the - * delete-on-close status returned by the Trans2QueryFileInfo will not - * be set. The delete-on-close status will only be set when the above - * file handle is closed. - * - * 2) If a file is not opened with delete-on-close create options but the - * delete-on-close is set via Trans2SetFileInfo/DispositionInfo, the - * delete-on-close status returned by Trans2QueryFileInfo will be set - * immediately. - */ -smb_sdrc_t -smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa) -{ - static smb_attr_t pipe_attr; - smb_attr_t file_attr; - unsigned short infolev, dattr = 0; - u_offset_t datasz = 0, allocsz = 0; - smb_attr_t *ap = NULL; - char *namep = NULL; - char *filename = NULL; - int filename_len = 0; - smb_node_t *node = NULL; /* only set for SMB_FTYPE_DISK files */ - smb_node_t *dnode = NULL; - unsigned char delete_on_close = 0; - unsigned char is_dir = 0; - char *filebuf = NULL; - char short_name[SMB_SHORTNAMELEN]; - char name83[SMB_SHORTNAMELEN]; - int rc; - - if (smb_mbc_decodef(&xa->req_param_mb, "ww", &sr->smb_fid, - &infolev) != 0) { - return (SDRC_ERROR); - } - - smbsr_lookup_file(sr); - if (sr->fid_ofile == NULL) { - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); - return (SDRC_ERROR); - } - - switch (sr->fid_ofile->f_ftype) { - case SMB_FTYPE_DISK: - { - /* The node is only valid for SMB_FTYPE_DISK files. */ - node = sr->fid_ofile->f_node; - - /* - * For some reason NT will not show the security tab in the root - * directory of a mapped drive unless the filename length is - * greater than one. - * This may be a NT vs Windows9x UNICODE check. - * So we hack the length here to persuade NT to show the tab. It - * should be safe because of the null terminator character. - */ - /* be careful here we need od_name now rather than node_name */ - /* do we want to use node_name in the case of softlinks ?? */ - namep = node->od_name; - filename = namep; - filename_len = smb_ascii_or_unicode_strlen(sr, filename); - if (strcmp(namep, ".") == 0 && filename_len == 1) - filename_len = 2; - - ap = &file_attr; - if (smb_node_getattr(sr, node, ap) != 0) { - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - dattr = ap->sa_dosattr; - - if (smb_node_is_dir(node)) { - is_dir = 1; - datasz = allocsz = 0; - } else { - is_dir = 0; - datasz = ap->sa_vattr.va_size; - allocsz = ap->sa_vattr.va_nblocks * DEV_BSIZE; - } - - dnode = node->n_dnode; - 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 (delete_on_close && ap->sa_vattr.va_nlink > 0) - --(ap->sa_vattr.va_nlink); - - } - break; - - case SMB_FTYPE_MESG_PIPE: - { - /* The pipe is only valid for SMB_FTYPE_MESG_PIPE files */ - namep = sr->fid_ofile->f_pipe->p_name; - filename = namep; - filename_len = smb_ascii_or_unicode_strlen(sr, filename); - - ap = &pipe_attr; - ap->sa_vattr.va_nlink = 1; - dattr = FILE_ATTRIBUTE_NORMAL; - datasz = allocsz = 0; - - delete_on_close = 1; - is_dir = 0; - - /* some levels are not valid for pipes */ - switch (infolev) { - case SMB_QUERY_FILE_ALT_NAME_INFO: - case SMB_FILE_ALT_NAME_INFORMATION: - case SMB_QUERY_FILE_STREAM_INFO: - case SMB_FILE_STREAM_INFORMATION: - case SMB_QUERY_FILE_COMPRESSION_INFO: - case SMB_FILE_COMPRESSION_INFORMATION: - case SMB_FILE_ATTR_TAG_INFORMATION: - smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, - ERRDOS, ERROR_INVALID_PARAMETER); - return (SDRC_ERROR); - case SMB_INFO_QUERY_ALL_EAS: - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, - ERRDOS, ERROR_ACCESS_DENIED); - return (SDRC_ERROR); - } - } - break; - default: - smbsr_error(sr, 0, ERRDOS, ERRbadfile); - return (SDRC_ERROR); - } - - filebuf = kmem_alloc(MAXPATHLEN+1, KM_SLEEP); - - switch (infolev) { - case SMB_FILE_ACCESS_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_data_mb, "l", - sr->fid_ofile->f_granted_access); - break; - - case SMB_INFO_STANDARD: - if (datasz > UINT_MAX) - datasz = UINT_MAX; - if (allocsz > UINT_MAX) - allocsz = UINT_MAX; - - /* unlike other levels attributes should be 0 here for pipes */ - if (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE) - dattr = 0; - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_smb_datetimes(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "llw", - (uint32_t)datasz, (uint32_t)allocsz, dattr); - break; - - case SMB_INFO_QUERY_EA_SIZE: - if (datasz > UINT_MAX) - datasz = UINT_MAX; - if (allocsz > UINT_MAX) - allocsz = UINT_MAX; - - /* unlike other levels attributes should be 0 here for pipes */ - if (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE) - dattr = 0; - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_smb_datetimes(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "llwl", - (uint32_t)datasz, (uint32_t)allocsz, dattr, 0); - break; - - case SMB_INFO_QUERY_ALL_EAS: - case SMB_INFO_QUERY_EAS_FROM_LIST: - case SMB_FILE_EA_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); - break; - - case SMB_INFO_IS_NAME_VALID: - break; - - case SMB_QUERY_FILE_BASIC_INFO: - case SMB_FILE_BASIC_INFORMATION: - /* - * NT includes 6 undocumented bytes at the end of this - * response, which are required by NetBench 5.01. - * Similar change in smb_trans2_query_path_information.c. - */ - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_nt_times(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "w6.", dattr); - break; - - case SMB_QUERY_FILE_STANDARD_INFO: - case SMB_FILE_STANDARD_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - /* - * Add 2 bytes to pad data to long. It is - * necessary because Win2k expects the padded bytes. - */ - (void) smb_mbc_encodef(&xa->rep_data_mb, "qqlbb2.", - (uint64_t)allocsz, - (uint64_t)datasz, - ap->sa_vattr.va_nlink, - delete_on_close, - is_dir); - break; - - case SMB_QUERY_FILE_EA_INFO: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); - break; - - case SMB_QUERY_FILE_NAME_INFO: - case SMB_FILE_NAME_INFORMATION: - /* - * It looks like NT doesn't know what to do with the name "." - * so we convert it to "\\" to indicate the root directory. - * - * If the leading \ is missing, add it. - */ - if (strcmp(namep, ".") == 0) { - filename = "\\"; - filename_len = 2; - } else if (*namep != '\\') { - filename = filebuf; - (void) snprintf(filename, MAXNAMELEN + 1, "\\%s", - namep); - filename_len = - smb_ascii_or_unicode_strlen(sr, filename); - } else { - filename = namep; - filename_len = - smb_ascii_or_unicode_strlen(sr, filename); - } - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", sr, - filename_len, filename); - break; - - case SMB_QUERY_FILE_ALL_INFO: - case SMB_FILE_ALL_INFORMATION: - if (sr->fid_ofile->f_ftype == SMB_FTYPE_DISK) { - rc = smb_query_all_info_filename(sr->tid_tree, node, - filebuf, MAXPATHLEN); - if (rc != 0) { - smbsr_errno(sr, rc); - kmem_free(filebuf, MAXPATHLEN+1); - return (SDRC_ERROR); - } - filename = filebuf; - } else { - /* If the leading \ is missing, add it. */ - if (*namep != '\\') { - (void) snprintf(filebuf, MAXNAMELEN, - "\\%s", namep); - filename = filebuf; - } - } - filename_len = smb_ascii_or_unicode_strlen(sr, filename); - - /* - * There is a 6-byte pad between Attributes and AllocationSize, - * and a 2-byte pad after the Directory field. - */ - if (node) { - rc = smb_query_all_info_filename(sr->tid_tree, node, - filebuf, MAXPATHLEN); - if (rc != 0) { - smbsr_errno(sr, rc); - kmem_free(filebuf, MAXPATHLEN+1); - return (SDRC_ERROR); - } - filename = filebuf; - filename_len = smb_ascii_or_unicode_strlen(sr, - filename); - } - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_nt_times(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "w6.qqlbb2.l", - dattr, - (uint64_t)allocsz, - (uint64_t)datasz, - ap->sa_vattr.va_nlink, - delete_on_close, - is_dir, - 0); - - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", - sr, filename_len, filename); - break; - - case SMB_QUERY_FILE_ALT_NAME_INFO: - case SMB_FILE_ALT_NAME_INFORMATION: - /* - * If the shortname is generated by smb_mangle_name() - * it will be returned as the alternative name. - * Otherwise, convert the original name to upper-case - * and return it as the alternative name. - */ - (void) smb_mangle_name(ap->sa_vattr.va_nodeid, - filename, short_name, name83, 0); - if (*short_name == 0) { - (void) strlcpy(short_name, filename, SMB_SHORTNAMELEN); - (void) utf8_strupr(short_name); - } - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lU", sr, - mts_wcequiv_strlen(short_name), short_name); - break; - - case SMB_QUERY_FILE_STREAM_INFO: - case SMB_FILE_STREAM_INFORMATION: - { - struct smb_node *node = sr->fid_ofile->f_node; - if (dnode == NULL) { - kmem_free(filebuf, MAXPATHLEN+1); - smbsr_error(sr, 0, ERRDOS, ERRbadfile); - return (SDRC_ERROR); - } - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - if (SMB_IS_STREAM(node)) { - ASSERT(node->n_unode); - ASSERT(node->n_unode->n_magic == SMB_NODE_MAGIC); - ASSERT(node->n_unode->n_state != - SMB_NODE_STATE_DESTROYING); - - (void) smb_encode_stream_info(sr, xa, - node->n_unode, ap); - } else { - (void) smb_encode_stream_info(sr, xa, node, ap); - } - break; - } - case SMB_QUERY_FILE_COMPRESSION_INFO: - case SMB_FILE_COMPRESSION_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "qwbbb3.", - datasz, 0, 0, 0, 0); - break; - - case SMB_FILE_INTERNAL_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "q", - ap->sa_vattr.va_nodeid); - break; - - case SMB_FILE_ATTR_TAG_INFORMATION: - /* - * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the - * second dword should be the reparse tag. Otherwise - * the tag value should be set to zero. - * We don't support reparse points, so we set the tag - * to zero. - */ - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "ll", - (uint32_t)dattr, 0); - break; - - default: - kmem_free(filebuf, MAXPATHLEN+1); - smbsr_error(sr, 0, ERRDOS, ERRunknownlevel); - return (SDRC_ERROR); - } - - kmem_free(filebuf, MAXPATHLEN+1); - return (SDRC_SUCCESS); -} - -/* - * smb_encode_stream_info - * - * This function encodes the streams information for both T2QueryFileInfo - * and T2QueryPathInfo. The rules about how to do this are not documented. - * They have been derived using observed NT behaviour and the IR's listed - * below. - * - * IR101680: ArcServe2000 problem. ArcServe doesn't like the null- - * stream data on directories that don't have any associated streams. - * - * IR103484 and KB article Q234765: Citrix problem. If there are no - * streams, only return the unnamed stream data if the target is a - * file. The Citrix Metaframe cdm.sys driver crashes the Windows server, - * on which it's running, if it receives the unexpected stream data - * for a directory. - * - * If there are streams, on files or directories, we need to return - * them to support Mac/DAVE clients. Mac clients make this request - * to see if there is a comment stream. If we don't provide the - * information, the client won't try to access the comment stream. - * - * If the target is a file: - * 1. If there are no named streams, the response should still contain - * an entry for the unnamed stream. - * 2. If there are named streams, the response should contain an entry - * for the unnamed stream followed by the entries for the named - * streams. - * - * If the target is a directory: - * 1. If there are no streams, the response is complete. Directories - * do not report the unnamed stream. - * 2. If there are streams, the response should contain entries for - * those streams but there should not be an entry for the unnamed - * stream. - * - * Note that the stream name lengths exclude the null terminator but - * the field lengths (i.e. next offset calculations) need to include - * the null terminator and be padded to a multiple of 8 bytes. The - * last entry does not seem to need any padding. - * - * If an error is encountered when trying to read the directory - * entries (smb_fsop_stream_readdir) it is treated as if there are - * no [more] directory entries. The entries that have been read so - * far are returned and no error is reported. - * - * Offset calculation: - * 2 dwords + 2 quadwords => 4 + 4 + 8 + 8 => 24 - */ -void -smb_encode_stream_info( - struct smb_request *sr, - struct smb_xa *xa, - struct smb_node *fnode, - smb_attr_t *attr) -{ - char *stream_name; - uint32_t next_offset; - uint32_t stream_nlen; - uint32_t pad; - u_offset_t datasz, allocsz; - boolean_t is_dir; - smb_streaminfo_t *sinfo, *sinfo_next; - int rc = 0; - boolean_t done = B_FALSE; - boolean_t eos = B_FALSE; - uint16_t odid; - smb_odir_t *od = NULL; - - sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); - sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP); - is_dir = (attr->sa_vattr.va_type == VDIR); - datasz = attr->sa_vattr.va_size; - allocsz = attr->sa_vattr.va_nblocks * DEV_BSIZE; - - odid = smb_odir_openat(sr, fnode); - if (odid != 0) - od = smb_tree_lookup_odir(sr->tid_tree, odid); - if (od != NULL) - rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos); - - if ((od == NULL) || (rc != 0) || (eos)) - done = B_TRUE; - - /* - * If not a directory, encode an entry for the unnamed stream. - */ - if (!is_dir) { - stream_name = "::$DATA"; - stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name); - - if (done) - next_offset = 0; - else - next_offset = 24 + stream_nlen + - smb_ascii_or_unicode_null_len(sr); - - (void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu", sr, - next_offset, stream_nlen, datasz, allocsz, stream_name); - } - - /* - * Since last packet does not have a pad we need to check - * for the next stream before we encode the current one - */ - while (!done) { - stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name); - sinfo_next->si_name[0] = 0; - - rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos); - if ((rc != 0) || (eos)) { - done = B_TRUE; - next_offset = 0; - pad = 0; - } else { - next_offset = 24 + stream_nlen + - smb_ascii_or_unicode_null_len(sr); - pad = smb_pad_align(next_offset, 8); - next_offset += pad; - } - (void) smb_mbc_encodef(&xa->rep_data_mb, "%llqqu#.", - sr, next_offset, stream_nlen, - sinfo->si_size, sinfo->si_alloc_size, - sinfo->si_name, pad); - - (void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t)); - } - - kmem_free(sinfo, sizeof (smb_streaminfo_t)); - kmem_free(sinfo_next, sizeof (smb_streaminfo_t)); - if (od) { - smb_odir_close(od); - smb_odir_release(od); - } -} - -/* - * smb_pad_align - * - * Returns the number of bytes required to get pad an offset to the - * specified alignment. - */ -uint32_t -smb_pad_align(uint32_t offset, uint32_t align) -{ - uint32_t pad = offset % align; - - if (pad != 0) - pad = align - pad; - - return (pad); -} - -/* - * smb_encode_smb_datetimes - * - * Encode timestamps in the SMB_DATE / SMB_TIME format. - */ -void -smb_encode_smb_datetimes(smb_request_t *sr, smb_xa_t *xa, smb_attr_t *attr) -{ - if ((sr->fid_ofile) && - (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)) { - (void) smb_mbc_encodef(&xa->rep_data_mb, "lll", 0, 0, 0); - return; - } - - (void) smb_mbc_encodef(&xa->rep_data_mb, - ((sr->session->native_os == NATIVE_OS_WIN95) ? "YYY" : "yyy"), - smb_gmt2local(sr, attr->sa_crtime.tv_sec), - smb_gmt2local(sr, attr->sa_vattr.va_atime.tv_sec), - smb_gmt2local(sr, attr->sa_vattr.va_mtime.tv_sec)); -} - -/* - * smb_encode_nt_times - * - * Encode timestamps in LARGE INTEGER format NT Times. - */ -void -smb_encode_nt_times(smb_request_t *sr, smb_xa_t *xa, smb_attr_t *attr) -{ - if ((sr->fid_ofile) && - (sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE)) { - (void) smb_mbc_encodef(&xa->rep_data_mb, "qqqq", 0, 0, 0, 0); - return; - } - - (void) smb_mbc_encodef(&xa->rep_data_mb, "TTTT", - &attr->sa_crtime, - &attr->sa_vattr.va_atime, - &attr->sa_vattr.va_mtime, - &attr->sa_vattr.va_ctime); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c deleted file mode 100644 index 078fd4e4af..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_query_path_info.c +++ /dev/null @@ -1,663 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - - -/* - * SMB: trans2_query_path_information - * - * This request is used to get information about a specific file or - * subdirectory. - * - * Client Request Value - * ========================== ========================================= - * - * WordCount 15 - * MaxSetupCount 0 - * SetupCount 1 - * Setup[0] TRANS2_QUERY_PATH_INFORMATION - * - * Parameter Block Encoding Description - * ========================== ========================================= - * - * USHORT InformationLevel; Level of information requested - * ULONG Reserved; Must be zero - * STRING FileName; File or directory name - * - * The following InformationLevels may be requested: - * - * Information Level Value - * - * ================================ ===== - * - * SMB_INFO_STANDARD 1 - * SMB_INFO_QUERY_EA_SIZE 2 - * SMB_INFO_QUERY_EAS_FROM_LIST 3 - * SMB_INFO_QUERY_ALL_EAS 4 - * SMB_INFO_IS_NAME_VALID 6 - * SMB_QUERY_FILE_BASIC_INFO 0x101 - * SMB_QUERY_FILE_STANDARD_INFO 0x102 - * SMB_QUERY_FILE_EA_INFO 0x103 - * SMB_QUERY_FILE_NAME_INFO 0x104 - * SMB_QUERY_FILE_ALL_INFO 0x107 - * SMB_QUERY_FILE_ALT_NAME_INFO 0x108 - * SMB_QUERY_FILE_STREAM_INFO 0x109 - * SMB_QUERY_FILE_COMPRESSION_INFO 0x10B - * SMB_FILE_INTERNAL_INFORMATION 1006 - * - * The requested information is placed in the Data portion of the - * transaction response. For the information levels greater than 0x100, - * the transaction response has 1 parameter word which should be ignored by - * the client. - * - * The following sections describe the InformationLevel dependent encoding - * of the data part of the transaction response. - * - * 4.2.14.1 SMB_INFO_STANDARD & SMB_INFO_QUERY_EA_SIZE - * - * Data Block Encoding Description - * =============================== ==================================== - * - * SMB_DATE CreationDate; Date when file was created - * SMB_TIME CreationTime; Time when file was created - * SMB_DATE LastAccessDate; Date of last file access - * SMB_TIME LastAccessTime; Time of last file access - * SMB_DATE LastWriteDate; Date of last write to the file - * SMB_TIME LastWriteTime; Time of last write to the file - * ULONG DataSize; File Size - * ULONG AllocationSize; Size of filesystem allocation unit - * USHORT Attributes; File Attributes - * ULONG EaSize; Size of file's EA information - * (SMB_INFO_QUERY_EA_SIZE) - * - * 4.2.14.2 SMB_INFO_QUERY_EAS_FROM_LIST & SMB_INFO_QUERY_ALL_EAS - * - * Response Field Value - * ==================== =============================================== - * - * MaxDataCount Length of EAlist found (minimum value is 4) - * - * Parameter Block Description - * Encoding =============================================== - * ==================== - * - * USHORT EaErrorOffset Offset into EAList of EA error - * - * Data Block Encoding Description - * ==================== =============================================== - * - * ULONG ListLength; Length of the remaining data - * UCHAR EaList[] The extended attributes list - * - * 4.2.14.3 SMB_INFO_IS_NAME_VALID - * - * This requests checks to see if the name of the file contained in the - * request's Data field has a valid path syntax. No parameters or data are - * returned on this information request. An error is returned if the syntax - * of the name is incorrect. Success indicates the server accepts the path - * syntax, but it does not ensure the file or directory actually exists. - * - * 4.2.14.4 SMB_QUERY_FILE_BASIC_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * - * LARGE_INTEGER CreationTime; Time when file was created - * LARGE_INTEGER LastAccessTime; Time of last file access - * LARGE_INTEGER LastWriteTime; Time of last write to the file - * LARGE_INTEGER ChangeTime Time when file was last changed - * USHORT Attributes; File Attributes - * - * 4.2.14.5 SMB_QUERY_FILE_STANDARD_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * - * LARGE_INTEGER AllocationSize Allocated size of the file in number - * of bytes - * LARGE_INTEGER EndofFile; Offset to the first free byte in the - * file - * ULONG NumberOfLinks Number of hard links to the file - * BOOLEAN DeletePending Indicates whether the file is marked - * for deletion - * BOOLEAN Directory Indicates whether the file is a - * directory - * - * 4.2.14.6 SMB_QUERY_FILE_EA_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * - * ULONG EASize Size of the file's extended - * attributes in number of bytes - * - * 4.2.14.7 SMB_QUERY_FILE_NAME_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * - * ULONG FileNameLength Length of the file name in number of - * bytes - * STRING FileName Name of the file - * - * 4.2.14.8 SMB_QUERY_FILE_ALL_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * - * LARGE_INTEGER CreationTime; Time when file was created - * LARGE_INTEGER LastAccessTime; Time of last file access - * LARGE_INTEGER LastWriteTime; Time of last write to the file - * LARGE_INTEGER ChangeTime Time when file was last changed - * USHORT Attributes; File Attributes - * LARGE_INTEGER AllocationSize Allocated size of the file in number - * of bytes - * LARGE_INTEGER EndofFile; Offset to the first free byte in the - * file - * ULONG NumberOfLinks Number of hard links to the file - * BOOLEAN DeletePending Indicates whether the file is marked - * for deletion - * BOOLEAN Directory Indicates whether the file is a - * directory - * LARGE_INTEGER Index Number A file system unique identifier - * ULONG EASize Size of the file's extended - * attributes in number of bytes - * ULONG AccessFlags Access that a caller has to the - * file; Possible values and meanings - * are specified below - * LARGE_INTEGER Index Number A file system unique identifier - * LARGE_INTEGER CurrentByteOffset Current byte offset within the file - * ULONG Mode Current Open mode of the file handle - * to the file; possible values and - * meanings are detailed below - * ULONG AlignmentRequirement Buffer Alignment required by device; - * possible values detailed below - * ULONG FileNameLength Length of the file name in number of - * bytes - * STRING FileName Name of the file - * - * The AccessFlags specifies the access permissions a caller has to the - * file and can have any suitable combination of the following values: - * - * Value Meaning - * - * ILE_READ_DATA 0x00000001 Data can be read from the file - * ILE_WRITE_DATA 0x00000002 Data can be written to the file - * ILE_APPEND_DATA 0x00000004 Data can be appended to the file - * ILE_READ_EA 0x00000008 Extended attributes associated - * with the file can be read - * ILE_WRITE_EA 0x00000010 Extended attributes associated - * with the file can be written - * ILE_EXECUTE 0x00000020 Data can be read into memory from - * the file using system paging I/O - * ILE_READ_ATTRIBUTES 0x00000080 Attributes associated with the - * file can be read - * ILE_WRITE_ATTRIBUTES 0x00000100 Attributes associated with the - * file can be written - * ELETE 0x00010000 The file can be deleted - * EAD_CONTROL 0x00020000 The access control list and - * ownership associated with the - * file can be read - * RITE_DAC 0x00040000 The access control list and - * ownership associated with the - * file can be written. - * RITE_OWNER 0x00080000 Ownership information associated - * with the file can be written - * YNCHRONIZE 0x00100000 The file handle can waited on to - * synchronize with the completion - * of an input/output request - * - * The Mode field specifies the mode in which the file is currently opened. - * The possible values may be a suitable and logical combination of the - * following: - * - * Value Meaning - * - * FILE_WRITE_THROUGH 0x00000002 File is opened in mode - * where data is written to - * file before the driver - * completes a write request - * FILE_SEQUENTIAL_ONLY 0x00000004 All access to the file is - * sequential - * FILE_SYNCHRONOUS_IO_ALERT 0x00000010 All operations on the - * file are performed - * synchronously - * FILE_SYNCHRONOUS_IO_NONALER 0x00000020 All operations on the - * T file are to be performed - * synchronously. Waits in - * the system to synchronize - * I/O queuing and - * completion are not - * subject to alerts. - * - * The AlignmentRequirement field specifies buffer alignment required by - * the device and can have any one of the following values: - * - * Value Meaning - * - * FILE_BYTE_ALIGNMENT 0x00000000 The buffer needs to be aligned - * on a byte boundary - * FILE_WORD_ALIGNMENT 0x00000001 The buffer needs to be aligned - * on a word boundary - * FILE_LONG_ALIGNMENT 0x00000003 The buffer needs to be aligned - * on a 4 byte boundary - * FILE_QUAD_ALIGNMENT 0x00000007 The buffer needs to be aligned - * on an 8 byte boundary - * FILE_OCTA_ALIGNMENT 0x0000000f The buffer needs to be aligned - * on a 16 byte boundary - * FILE_32_BYTE_ALIGNMENT 0x0000001f The buffer needs to be aligned - * on a 32 byte boundary - * FILE_64_BYTE_ALIGNMENT 0x0000003f The buffer needs to be aligned - * on a 64 byte boundary - * FILE_128_BYTE_ALIGNMENT 0x0000007f The buffer needs to be aligned - * on a 128 byte boundary - * FILE_256_BYTE_ALIGNMENT 0x000000ff The buffer needs to be aligned - * on a 256 byte boundary - * FILE_512_BYTE_ALIGNMENT 0x000001ff The buffer needs to be aligned - * on a 512 byte boundary - * - * 4.2.14.9 SMB_QUERY_FILE_ALT_NAME_INFO - * - * Data Block Encoding Description - * ===================== ================================= - * ULONG FileNameLength Length of the file name in number of bytes - * STRING FileName Name of the file - * - * 4.2.14.10 SMB_QUERY_FILE_STREAM_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * ULONG NextEntryOffset Offset to the next entry (in bytes) - * ULONG StreamNameLength Length of the stream name in # of bytes - * LARGE_INTEGER StreamSize Size of the stream in number of bytes - * LARGE_INTEGER AllocationSize Allocated size of stream in bytes - * STRING FileName Name of the stream - * - * 4.2.14.11 SMB_QUERY_FILE_COMPRESSION_INFO - * - * Data Block Encoding Description - * =============================== ==================================== - * LARGE_INTEGER Size of the compressed file in - * CompressedFileSize number of bytes - * USHORT CompressionFormat compression algorithm used - * UCHAR CompressionUnitShift Size of the stream in number of bytes - * UCHAR ChunkShift Allocated size of the stream in # of bytes - * UCHAR ClusterShift Allocated size of the stream in # of bytes - * UCHAR Reserved[3] Name of the stream - * - * typedef struct { - * LARGE_INTEGER CompressedFileSize; - * USHORT CompressionFormat; - * UCHAR CompressionUnitShift; - * UCHAR ChunkShift; - * UCHAR ClusterShift; - * UCHAR Reserved[3]; - * } FILE_COMPRESSION_INFORMATION; - */ - -#include <smbsrv/smb_incl.h> -#include <smbsrv/msgbuf.h> -#include <smbsrv/smb_vops.h> -#include <smbsrv/smb_fsops.h> - -int smb_query_all_info_filename(smb_tree_t *, smb_node_t *, char *, size_t); -extern void smb_encode_smb_datetimes(smb_request_t *, smb_xa_t *, smb_attr_t *); -extern void smb_encode_nt_times(smb_request_t *, smb_xa_t *, smb_attr_t *); - -/* - * Function: int smb_com_trans2_query_path_information(struct smb_request *) - */ -smb_sdrc_t -smb_com_trans2_query_path_information(struct smb_request *sr, struct smb_xa *xa) -{ - char *path, *alt_nm_ptr; - int rc; - u_offset_t datasz, allocsz; - unsigned short infolev, dattr; - smb_attr_t *ap, ret_attr; - struct smb_node *dir_node; - struct smb_node *node; - char *name, *namep; - char short_name[SMB_SHORTNAMELEN]; - char name83[SMB_SHORTNAMELEN]; - unsigned char is_dir; - unsigned char delete_on_close; - int len; - - 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); - } - - /* - * Some MS clients pass NULL file names - * NT interprets this as "\" - */ - if ((len = strlen(path)) == 0) - path = "\\"; - else { - if ((len > 1) && (path[len - 1] == '\\')) { - /* - * Remove the terminating slash to prevent - * sending back '.' instead of path name. - */ - path[len - 1] = 0; - } - } - - ap = &ret_attr; - name = kmem_zalloc(MAXPATHLEN, KM_SLEEP); - - if ((rc = smb_pathname_reduce(sr, sr->user_cr, path, - sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dir_node, name)) - != 0) { - kmem_free(name, MAXPATHLEN); - if (rc == ENOENT) - smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, - ERRDOS, ERROR_FILE_NOT_FOUND); - else - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - if ((rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, dir_node, name, &node)) != 0) { - smb_node_release(dir_node); - kmem_free(name, MAXPATHLEN); - - if (rc == ENOENT) - smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, - ERRDOS, ERROR_FILE_NOT_FOUND); - else - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - smb_node_release(dir_node); - - if (smb_node_getattr(sr, node, ap) != 0) { - smb_node_release(node); - kmem_free(name, MAXPATHLEN); - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - (void) strcpy(name, node->od_name); - namep = node->od_name; - dattr = ap->sa_dosattr; - - if (smb_node_is_dir(node)) { - is_dir = 1; - /* Win2K and NT reply with the size of directory file */ - datasz = allocsz = 0; - } else { - is_dir = 0; - datasz = ap->sa_vattr.va_size; - allocsz = ap->sa_vattr.va_nblocks * DEV_BSIZE; - } - - 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 (delete_on_close && ap->sa_vattr.va_nlink > 0) - --(ap->sa_vattr.va_nlink); - - - - switch (infolev) { - case SMB_INFO_STANDARD: - if (datasz > UINT_MAX) - datasz = UINT_MAX; - if (allocsz > UINT_MAX) - allocsz = UINT_MAX; - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_smb_datetimes(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "llw", - (uint32_t)datasz, (uint32_t)allocsz, dattr); - break; - - case SMB_INFO_QUERY_EA_SIZE: - if (datasz > UINT_MAX) - datasz = UINT_MAX; - if (allocsz > UINT_MAX) - allocsz = UINT_MAX; - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_smb_datetimes(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "llwl", - (uint32_t)datasz, (uint32_t)allocsz, dattr, 0); - break; - - case SMB_INFO_QUERY_EAS_FROM_LIST: - case SMB_INFO_QUERY_ALL_EAS: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); - break; - - case SMB_INFO_IS_NAME_VALID: - break; - - case SMB_QUERY_FILE_BASIC_INFO: - case SMB_FILE_BASIC_INFORMATION: - /* - * NT includes 6 undocumented bytes at the end of this - * response, which are required by NetBench 5.01. - * Similar change in smb_trans2_query_file_information.c. - */ - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_nt_times(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "w6.", dattr); - break; - - case SMB_QUERY_FILE_STANDARD_INFO: - case SMB_FILE_STANDARD_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - /* - * Add 2 bytes to pad data to long. It is - * necessary because Win2k expects the padded bytes. - */ - (void) smb_mbc_encodef(&xa->rep_data_mb, "qqlbb2.", - (uint64_t)allocsz, - (uint64_t)datasz, - ap->sa_vattr.va_nlink, - delete_on_close, - (char)(ap->sa_vattr.va_type == VDIR)); - break; - - case SMB_QUERY_FILE_EA_INFO: - case SMB_FILE_EA_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "l", 0); - break; - - case SMB_QUERY_FILE_NAME_INFO: - case SMB_FILE_NAME_INFORMATION: - /* If the leading \ is missing, add it. */ - if (*namep != '\\') { - (void) snprintf(name, MAXNAMELEN, "\\%s", namep); - namep = name; - } - len = smb_ascii_or_unicode_strlen(sr, namep); - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", sr, len, namep); - break; - - case SMB_QUERY_FILE_ALL_INFO: - case SMB_FILE_ALL_INFORMATION: - rc = smb_query_all_info_filename(sr->tid_tree, node, - name, MAXPATHLEN); - if (rc != 0) { - smbsr_errno(sr, rc); - smb_node_release(node); - kmem_free(name, MAXPATHLEN); - return (SDRC_ERROR); - } - - /* - * There is a 6-byte pad between Attributes and AllocationSize, - * and a 2-byte pad after the Directory field. - */ - rc = smb_query_all_info_filename(sr->tid_tree, node, - name, MAXPATHLEN); - if (rc != 0) { - smbsr_errno(sr, rc); - smb_node_release(node); - kmem_free(name, MAXPATHLEN); - return (SDRC_ERROR); - } - - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_nt_times(sr, xa, ap); - (void) smb_mbc_encodef(&xa->rep_data_mb, "w6.qqlbb2.l", - dattr, - (uint64_t)allocsz, - (uint64_t)datasz, - ap->sa_vattr.va_nlink, - 0, - is_dir, - 0); - - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lu", sr, - smb_ascii_or_unicode_strlen(sr, name), name); - break; - - case SMB_QUERY_FILE_ALT_NAME_INFO: - case SMB_FILE_ALT_NAME_INFORMATION: - /* - * If the shortname is generated by smb_mangle_name() - * it will be returned as the alternative name. - * Otherwise, convert the original name to upper-case - * and return it as the alternative name. - */ - (void) smb_mangle_name(ap->sa_vattr.va_nodeid, - name, short_name, name83, 0); - alt_nm_ptr = ((*short_name == 0) ? - utf8_strupr(name) : short_name); - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "%lU", sr, - mts_wcequiv_strlen(alt_nm_ptr), alt_nm_ptr); - break; - - case SMB_QUERY_FILE_STREAM_INFO: - case SMB_FILE_STREAM_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - smb_encode_stream_info(sr, xa, node, ap); - break; - - case SMB_QUERY_FILE_COMPRESSION_INFO: - case SMB_FILE_COMPRESSION_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, - "qwbbb3.", datasz, 0, 0, 0, 0); - break; - - case SMB_FILE_INTERNAL_INFORMATION: - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "q", - ap->sa_vattr.va_nodeid); - break; - - case SMB_FILE_ATTR_TAG_INFORMATION: - /* - * If dattr includes FILE_ATTRIBUTE_REPARSE_POINT, the - * second dword should be the reparse tag. Otherwise - * the tag value should be set to zero. - * We don't support reparse points, so we set the tag - * to zero. - */ - (void) smb_mbc_encodef(&xa->rep_param_mb, "w", 0); - (void) smb_mbc_encodef(&xa->rep_data_mb, "ll", - (uint32_t)dattr, 0); - break; - - default: - smb_node_release(node); - kmem_free(name, MAXPATHLEN); - smbsr_error(sr, 0, ERRDOS, ERRunknownlevel); - return (SDRC_ERROR); - } - - smb_node_release(node); - kmem_free(name, MAXPATHLEN); - return (SDRC_SUCCESS); -} - - -/* - * smb_query_all_info_filename - * - * This format of filename is only used by the ALL_INFO level. - * - * Determine the absolute pathname of 'node' within the share. - * For example if the node represents file "test1.txt" in directory - * "dir1" on share "share1", the path would be: \share1\dir1\test1.txt - * - * If node represents a named stream, construct the pathname for the - * associated unnamed stream then append the stream name. - */ -int -smb_query_all_info_filename(smb_tree_t *tree, smb_node_t *node, - char *buf, size_t buflen) -{ - char *sharename = tree->t_sharename; - int rc; - size_t len; - vnode_t *vp; - - len = snprintf(buf, buflen, "\\%s", sharename); - if (len == (buflen - 1)) - return (ENAMETOOLONG); - - buf += len; - buflen -= len; - - if (SMB_IS_STREAM(node)) - vp = node->n_unode->vp; - else - vp = node->vp; - - rc = vnodetopath(tree->t_snode->vp, vp, buf, buflen, kcred); - if (rc == 0) { - (void) strsubst(buf, '/', '\\'); - - if (SMB_IS_STREAM(node)) - (void) strlcat(buf, node->od_name, buflen); - } - - return (rc); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c deleted file mode 100644 index c57c58d8aa..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_file_information.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * SMB: trans2_set_file_information - * - * This request is used to set information about a specific file or - * subdirectory given a handle to the file or subdirectory. - * - * Client Request Value - * ========================== ========================================== - * - * WordCount 15 - * MaxSetupCount 0 - * SetupCount 1 - * Setup[0] TRANS2_SET_FILE_INFORMATION - * - * Parameter Block Encoding Description - * ========================== ========================================== - * - * USHORT Fid; Handle of file for request - * USHORT InformationLevel; Level of information requested - * USHORT Reserved; Ignored by the server - * - * The following InformationLevels may be set: - * - * Information Level Value - * ================================ ===== - * - * SMB_INFO_STANDARD 1 - * SMB_INFO_QUERY_EA_SIZE 2 - * SMB_SET_FILE_BASIC_INFO 0x101 - * SMB_SET_FILE_DISPOSITION_INFO 0x102 - * SMB_SET_FILE_ALLOCATION_INFO 0x103 - * SMB_SET_FILE_END_OF_FILE_INFO 0x104 - * - * The two levels below 0x101 are as described in the - * NT_SET_PATH_INFORMATION transaction. The requested information is - * placed in the Data portion of the transaction response. For the - * information levels greater than 0x100, the transaction response has 1 - * parameter word which should be ignored by the client. - * - * 4.2.17.1 SMB_FILE_DISPOSITION_INFO - * - * Response Field Value - * ==================== =============================================== - * - * BOOLEAN A boolean which is TRUE if the file is marked - * FileIsDeleted for deletion - * - * 4.2.17.2 SMB_FILE_ALLOCATION_INFO - * - * Response Field Value - * ==================== =============================================== - * - * LARGE_INTEGER File Allocation size in number of bytes - * - * 4.2.17.3 SMB_FILE_END_OF_FILE_INFO - * - * Response Field Value - * ==================== =============================================== - * - * LARGE_INTEGER The total number of bytes that need to be - * traversed from the beginning of the file in - * order to locate the end of the file - * - * Undocumented things: - * Poorly documented information levels. Information must be infered - * from other commands. - * - * NULL Attributes means don't set them. NT sets the high bit to - * set attributes to 0. - */ - -#include <smbsrv/smb_incl.h> - -/* - * smb_com_trans2_set_file_information - */ -smb_sdrc_t -smb_com_trans2_set_file_information(struct smb_request *sr, struct smb_xa *xa) -{ - smb_trans2_setinfo_t *info; - smb_error_t smberr; - uint32_t status; - int rc; - - info = kmem_zalloc(sizeof (smb_trans2_setinfo_t), KM_SLEEP); - info->ts_xa = xa; - - rc = smb_mbc_decodef(&xa->req_param_mb, "ww", &sr->smb_fid, - &info->level); - if (rc != 0) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - return (SDRC_ERROR); - } - - if (!STYPE_ISDSK(sr->tid_tree->t_res_type) || - SMB_TREE_IS_READONLY(sr)) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, - ERRDOS, ERROR_ACCESS_DENIED); - return (SDRC_ERROR); - } - - smbsr_lookup_file(sr); - if (sr->fid_ofile == NULL) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); - return (SDRC_ERROR); - } - - sr->user_cr = smb_ofile_getcred(sr->fid_ofile); - - info->node = sr->fid_ofile->f_node; - - if (info->node == 0 || - !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { - cmn_err(CE_NOTE, "SmbT2SetFileInfo: access denied"); - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, - ERRDOS, ERROR_ACCESS_DENIED); - return (SDRC_ERROR); - } - - status = smb_trans2_set_information(sr, info, &smberr); - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - - if (status == NT_STATUS_DATA_ERROR) - return (SDRC_ERROR); - - if (status != NT_STATUS_SUCCESS) { - smbsr_error(sr, smberr.status, smberr.errcls, smberr.errcode); - return (SDRC_ERROR); - } - - return (SDRC_SUCCESS); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c deleted file mode 100644 index 8c8efeb7a0..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_information.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * This file contains the common code used by - * Trans2SetFileInfo and Trans2SetPathInfo SMBs. - */ - -#include <smbsrv/smb_incl.h> -#include <smbsrv/smb_fsops.h> - -static uint32_t smb_set_standard_info(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -static uint32_t smb_set_basic_info(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -static uint32_t smb_set_disposition_info(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -static uint32_t smb_set_alloc_info(smb_request_t *sr, - smb_trans2_setinfo_t *, smb_error_t *); - -/*LINTED E_STATIC_UNUSED*/ -static uint32_t smb_set_mac_info(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -/*LINTED E_STATIC_UNUSED*/ -static uint32_t smb_set_mac_addappl(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -/*LINTED E_STATIC_UNUSED*/ -static uint32_t smb_set_mac_rmvappl(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -/*LINTED E_STATIC_UNUSED*/ -static uint32_t smb_set_mac_addicon(smb_request_t *, - smb_trans2_setinfo_t *, smb_error_t *); - -static unsigned short smb_info_passthru(unsigned short infolevel); - -/* - * smb_trans2_set_information - * - * This is a common function called by both Trans2SetFileInfo - * and Trans2SetPathInfo. - */ -uint32_t -smb_trans2_set_information(smb_request_t *sr, smb_trans2_setinfo_t *info, - smb_error_t *smberr) -{ - info->level = smb_info_passthru(info->level); - - switch (info->level) { - case SMB_INFO_STANDARD: - return (smb_set_standard_info(sr, info, smberr)); - - case SMB_INFO_SET_EAS: - /* EAs not supported */ - return (NT_STATUS_SUCCESS); - - case SMB_SET_FILE_BASIC_INFO: - return (smb_set_basic_info(sr, info, smberr)); - - case SMB_SET_FILE_DISPOSITION_INFO: - return (smb_set_disposition_info(sr, info, smberr)); - - case SMB_SET_FILE_END_OF_FILE_INFO: - case SMB_SET_FILE_ALLOCATION_INFO: - return (smb_set_alloc_info(sr, info, smberr)); - - default: - break; - } - - smberr->status = NT_STATUS_INVALID_INFO_CLASS; - smberr->errcls = ERRDOS; - smberr->errcode = ERROR_INVALID_PARAMETER; - return (NT_STATUS_UNSUCCESSFUL); -} - -/* - * smb_info_passthru - * - * SMB_INFO_PASSTHROUGH - * If the server supports information level request passing through, - * the client may add the information level with SMB_INFO_PASSTHROUGH - * and submit the file information in NT data format instead of SMB - * data format. Please refer to MSDN for related NT file information - * data structure. - * - * SMB_INFO_PASSTHROUGH (1000) is defined in win32/cifs.h and the file - * information class values are defined in win32/ntifs.h. we have - * observed: - * 0x3EC = SMB_INFO_PASSTHROUGH + FileBasicInformation (4) - * 0x3F5 = SMB_INFO_PASSTHROUGH + FileDispositionInformation (13) - * 0x3FC = SMB_INFO_PASSTHROUGH + FileEndOfFileInformation (20) - * - * Based on network traces between two Win2K systems: - * FileBasicInformation <=> SMB_SET_FILE_BASIC_INFO - * FileDispositionInformation <=> SMB_SET_FILE_DISPOSITION_INFO - * FileEndOfFileInformation <=> SMB_SET_FILE_END_OF_FILE_INFO - */ -static unsigned short -smb_info_passthru(unsigned short infolevel) -{ - if (infolevel <= SMB_INFO_PASSTHROUGH) - return (infolevel); - - infolevel -= SMB_INFO_PASSTHROUGH; - - switch (infolevel) { - case FileBasicInformation: - return (SMB_SET_FILE_BASIC_INFO); - - case FileDispositionInformation: - return (SMB_SET_FILE_DISPOSITION_INFO); - - case FileEndOfFileInformation: - return (SMB_SET_FILE_END_OF_FILE_INFO); - - case FileAllocationInformation: - return (SMB_SET_FILE_ALLOCATION_INFO); - } - - return (infolevel); -} - -/* - * smb_set_standard_info - * - * Sets standard file/path information. - */ -static uint32_t -smb_set_standard_info(smb_request_t *sr, smb_trans2_setinfo_t *info, - smb_error_t *smberr) -{ - smb_attr_t attr; - uint32_t Creation, LastAccess, LastWrite; /* times */ - uint32_t status = NT_STATUS_SUCCESS; - smb_node_t *node = info->node; - int rc; - - if (smb_mbc_decodef(&info->ts_xa->req_data_mb, "yyy", - &Creation, /* CreationDate/Time */ - &LastAccess, /* LastAccessDate/Time */ - &LastWrite) != 0) { /* LastWriteDate/Time */ - return (NT_STATUS_DATA_ERROR); - } - - bzero(&attr, sizeof (smb_attr_t)); - - /* - * The behaviour when the time field is set to -1 - * is not documented but is generally treated like 0, - * meaning that that server file system assigned value - * need not be changed. - */ - if (LastWrite != 0 && LastWrite != (uint32_t)-1) { - attr.sa_vattr.va_mtime.tv_sec = smb_local2gmt(sr, LastWrite); - attr.sa_mask |= SMB_AT_MTIME; - } - - if (Creation != 0 && Creation != (uint32_t)-1) { - attr.sa_crtime.tv_sec = smb_local2gmt(sr, Creation); - attr.sa_mask |= SMB_AT_CRTIME; - } - - if (LastAccess != 0 && LastAccess != (uint32_t)-1) { - attr.sa_vattr.va_atime.tv_sec = smb_local2gmt(sr, LastAccess); - attr.sa_mask |= SMB_AT_ATIME; - } - - rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); - if (rc) { - smbsr_map_errno(rc, smberr); - status = NT_STATUS_UNSUCCESSFUL; - } - - return (status); -} - -/* - * smb_set_basic_info - * - * Sets basic file/path information. - */ -static uint32_t -smb_set_basic_info(smb_request_t *sr, smb_trans2_setinfo_t *info, - smb_error_t *smberr) -{ - smb_attr_t attr; - uint64_t NT_Creation, NT_LastAccess, NT_LastWrite, NT_Change; - unsigned short Attributes; - smb_node_t *node = info->node; - uint32_t status = NT_STATUS_SUCCESS; - int rc; - - if (smb_mbc_decodef(&info->ts_xa->req_data_mb, "qqqqw", - &NT_Creation, /* CreationDate/Time */ - &NT_LastAccess, /* LastAccessDate/Time */ - &NT_LastWrite, /* LastWriteDate/Time */ - &NT_Change, /* LastWriteDate/Time */ - &Attributes) != 0) { /* File Attributes */ - return (NT_STATUS_DATA_ERROR); - } - - bzero(&attr, sizeof (smb_attr_t)); - - /* - * The behaviour when the time field is set to -1 - * is not documented but is generally treated like 0, - * meaning that that server file system assigned value - * need not be changed. - */ - if (NT_Change != 0 && NT_Change != (uint64_t)-1) { - (void) nt_to_unix_time(NT_Change, &attr.sa_vattr.va_ctime); - attr.sa_mask |= SMB_AT_CTIME; - } - - if (NT_Creation != 0 && NT_Creation != (uint64_t)-1) { - (void) nt_to_unix_time(NT_Creation, &attr.sa_crtime); - attr.sa_mask |= SMB_AT_CRTIME; - } - - if (NT_LastWrite != 0 && NT_LastWrite != (uint64_t)-1) { - (void) nt_to_unix_time(NT_LastWrite, &attr.sa_vattr.va_mtime); - attr.sa_mask |= SMB_AT_MTIME; - } - - if (NT_LastAccess != 0 && NT_LastAccess != (uint64_t)-1) { - (void) nt_to_unix_time(NT_LastAccess, &attr.sa_vattr.va_atime); - attr.sa_mask |= SMB_AT_ATIME; - } - - /* - * If Attributes are 0 this means that the file's attributes - * should be left unchanged. If the client wanted to 0 (clear) - * all of the attributes Attributes would be FILE_ATTRIBUTE_NORMAL. - * Note - this is different from SMBsetatr (SMBSetInformation). - * - * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the - * target is not a directory. - */ - if (Attributes != 0) { - if ((Attributes & FILE_ATTRIBUTE_DIRECTORY) && - (!smb_node_is_dir(node))) { - smberr->status = NT_STATUS_INVALID_PARAMETER; - smberr->errcls = ERRDOS; - smberr->errcode = ERROR_INVALID_PARAMETER; - return (NT_STATUS_UNSUCCESSFUL); - } - - attr.sa_dosattr = Attributes; - attr.sa_mask |= SMB_AT_DOSATTR; - } - - rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); - if (rc) { - smbsr_map_errno(rc, smberr); - status = NT_STATUS_UNSUCCESSFUL; - } - - return (status); -} - - -/* - * smb_set_alloc_info - * - * Sets file allocation/end_of_file info - */ -static uint32_t -smb_set_alloc_info(smb_request_t *sr, smb_trans2_setinfo_t *info, - smb_error_t *smberr) -{ - smb_attr_t attr; - uint64_t DataSize; - uint32_t status = NT_STATUS_SUCCESS; - smb_node_t *node = info->node; - int rc; - - if (smb_mbc_decodef(&info->ts_xa->req_data_mb, "q", &DataSize) != 0) - return (NT_STATUS_DATA_ERROR); - - bzero(&attr, sizeof (smb_attr_t)); - attr.sa_mask = SMB_AT_SIZE; - attr.sa_vattr.va_size = (u_offset_t)DataSize; - rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr); - if (rc) { - smbsr_map_errno(rc, smberr); - status = NT_STATUS_UNSUCCESSFUL; - } - - return (status); -} - -/* - * smb_set_disposition_info - * - * Set/Clear DELETE_ON_CLOSE flag for an open file. - * File should have been opened with DELETE access otherwise - * the operation is not permitted. - * - * NOTE: The node should be marked delete-on-close upon the receipt - * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set. - * It is different than both SmbNtCreateAndX and SmbNtTransact, which - * set delete-on-close on the ofile and defer setting the flag on the - * node until the file is closed. - * - * Observation of Windows 2000 indicates the following: - * - * 1) If a file is not opened with delete-on-close create options and - * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo) - * using that open file handle, any subsequent open requests will fail - * with DELETE_PENDING. - * - * 2) If a file is opened with delete-on-close create options and the - * client attempts to unset delete-on-close via Trans2SetFileInfo - * (SetDispositionInfo) prior to the file close, any subsequent open - * requests will still fail with DELETE_PENDING after the file is closed. - * - * 3) If a file is opened with delete-on-close create options and that - * file handle (not the last open handle and the only file handle - * with delete-on-close set) is closed. Any subsequent open requests - * will fail with DELETE_PENDING. Unsetting delete-on-close via - * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the - * node delete-on-close flag, which will result in the file not being - * removed even after the last file handle is closed. - */ -static uint32_t -smb_set_disposition_info(smb_request_t *sr, smb_trans2_setinfo_t *info, - smb_error_t *smberr) -{ - unsigned char mark_delete; - uint32_t flags = 0; - - if (smb_mbc_decodef(&info->ts_xa->req_data_mb, "b", &mark_delete) != 0) - return (NT_STATUS_DATA_ERROR); - - if ((sr->fid_ofile == NULL) || - !(smb_ofile_granted_access(sr->fid_ofile) & DELETE)) { - smberr->status = NT_STATUS_ACCESS_DENIED; - smberr->errcls = ERRDOS; - smberr->errcode = ERROR_ACCESS_DENIED; - return (NT_STATUS_UNSUCCESSFUL); - } - - if (mark_delete) { - if (SMB_TREE_SUPPORTS_CATIA(sr)) - flags |= SMB_CATIA; - - if (smb_node_set_delete_on_close(info->node, - sr->user_cr, flags)) { - smberr->status = NT_STATUS_CANNOT_DELETE; - smberr->errcls = ERRDOS; - smberr->errcode = ERROR_ACCESS_DENIED; - return (NT_STATUS_UNSUCCESSFUL); - } - } else { - smb_node_reset_delete_on_close(info->node); - } - return (NT_STATUS_SUCCESS); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c b/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c deleted file mode 100644 index 204018072d..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_trans2_set_path_information.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ -/* - * SMB: trans2_set_path_information - * - * This request is used to set information about a specific file or - * subdirectory. - * - * Client Request Value - * ========================== ========================================= - * - * WordCount 15 - * MaxSetupCount 0 - * SetupCount 1 - * Setup[0] TRANS2_SET_PATH_INFORMATION - * - * Parameter Block Encoding Description - * ========================== ========================================= - * - * USHORT InformationLevel; Level of information to set - * ULONG Reserved; Must be zero - * STRING FileName; File or directory name - * - * The following InformationLevels may be set: - * - * Information Level Value - * ========================== ========================================= - * - * SMB_INFO_STANDARD 1 - * SMB_INFO_QUERY_EA_SIZE 2 - * SMB_INFO_QUERY_ALL_EAS 4 - * - * The response formats are: - * - * 4.2.16.1 SMB_INFO_STANDARD & SMB_INFO_QUERY_EA_SIZE - * - * Parameter Block Encoding Description - * ================================== ================================= - * - * USHORT Reserved 0 - * - * Data Block Encoding Description - * ================================== ================================= - * - * SMB_DATE CreationDate; Date when file was created - * SMB_TIME CreationTime; Time when file was created - * SMB_DATE LastAccessDate; Date of last file access - * SMB_TIME LastAccessTime; Time of last file access - * SMB_DATE LastWriteDate; Date of last write to the file - * SMB_TIME LastWriteTime; Time of last write to the file - * ULONG DataSize; File Size - * ULONG AllocationSize; Size of filesystem allocation - * unit - * USHORT Attributes; File Attributes - * ULONG EaSize; Size of file's EA information - * (SMB_INFO_QUERY_EA_SIZE) - * - * 4.2.16.2 SMB_INFO_QUERY_ALL_EAS - * - * Response Field Value - * ==================== =============================================== - * - * MaxDataCount Length of FEAlist found (minimum value is 4) - * - * Parameter Block Description - * Encoding =============================================== - * ==================== - * - * USHORT EaErrorOffset Offset into EAList of EA error - * - * Data Block Encoding Description - * ==================== =============================================== - * - * ULONG ListLength; Length of the remaining data - * UCHAR EaList[] The extended attributes list - * - * Undocumented things: - * Poorly documented information levels. Information must be infered - * from other commands. - * - * NULL Attributes means don't set them. NT sets the high bit to - * set attributes to 0. - */ - -#include <smbsrv/smb_incl.h> -#include <smbsrv/smb_fsops.h> - -smb_sdrc_t -smb_com_trans2_set_path_information(struct smb_request *sr, struct smb_xa *xa) -{ - smb_trans2_setinfo_t *info; - struct smb_node *dir_node; - struct smb_node *ret_snode; - smb_error_t smberr; - uint32_t status; - int rc = 0; - - info = kmem_zalloc(sizeof (smb_trans2_setinfo_t), KM_SLEEP); - info->ts_xa = xa; - - if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u", sr, &info->level, - &info->path) != 0) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - return (SDRC_ERROR); - } - - if (!STYPE_ISDSK(sr->tid_tree->t_res_type) || - SMB_TREE_IS_READONLY(sr)) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_error(sr, NT_STATUS_ACCESS_DENIED, - ERRDOS, ERROR_ACCESS_DENIED); - return (SDRC_ERROR); - } - - rc = smb_pathname_reduce(sr, sr->user_cr, info->path, - sr->tid_tree->t_snode, sr->tid_tree->t_snode, - &dir_node, info->name); - - if (rc != 0) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS, - sr->tid_tree->t_snode, dir_node, info->name, &ret_snode); - - smb_node_release(dir_node); - - if (rc != 0) { - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - smbsr_errno(sr, rc); - return (SDRC_ERROR); - } - - if (smb_oplock_conflict(ret_snode, sr->session, NULL)) { - /* - * for the benefit of attribute setting later on - */ - (void) smb_oplock_break(ret_snode, sr->session, B_FALSE); - } - - info->node = ret_snode; - status = smb_trans2_set_information(sr, info, &smberr); - info->node = NULL; - smb_node_release(ret_snode); - kmem_free(info, sizeof (smb_trans2_setinfo_t)); - - if (status == NT_STATUS_DATA_ERROR) - return (SDRC_ERROR); - - if (status != NT_STATUS_SUCCESS) { - smbsr_error(sr, smberr.status, smberr.errcls, smberr.errcode); - return (SDRC_ERROR); - } - - return (SDRC_SUCCESS); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c index 66c3095851..ebe14f4cae 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_tree.c +++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c @@ -581,6 +581,11 @@ smb_tree_connect_disk(smb_request_t *sr, const char *sharename) break; } + /* ABE support */ + if (si->shr_flags & SMB_SHRF_ABE) + sr->arg.tcon.optional_support |= + SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM; + access = si->shr_access_value & SMB_SHRF_ACC_ALL; if (access == SMB_SHRF_ACC_RO) { @@ -1022,6 +1027,9 @@ smb_tree_get_flags(const smb_share_t *si, vfs_t *vfsp, smb_tree_t *tree) 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; diff --git a/usr/src/uts/common/fs/smbsrv/smb_util.c b/usr/src/uts/common/fs/smbsrv/smb_util.c index dab839fbf9..870c68ae51 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_util.c +++ b/usr/src/uts/common/fs/smbsrv/smb_util.c @@ -1613,32 +1613,64 @@ smb_idmap_batch_getmappings(smb_idmap_batch_t *sib) } uint64_t -unix_to_nt_time(timestruc_t *unix_time) +smb_time_unix_to_nt(timestruc_t *unix_time) { uint64_t nt_time; + if ((unix_time->tv_sec == 0) && (unix_time->tv_nsec == 0)) + return (0); + nt_time = unix_time->tv_sec; nt_time *= 10000000; /* seconds to 100ns */ nt_time += unix_time->tv_nsec / 100; return (nt_time + NT_TIME_BIAS); } -uint32_t -nt_to_unix_time(uint64_t nt_time, timestruc_t *unix_time) +void +smb_time_nt_to_unix(uint64_t nt_time, timestruc_t *unix_time) { uint32_t seconds; + ASSERT(unix_time); + + if ((nt_time == 0) || (nt_time == -1)) { + unix_time->tv_sec = 0; + unix_time->tv_nsec = 0; + return; + } + nt_time -= NT_TIME_BIAS; seconds = nt_time / 10000000; - if (unix_time) { - unix_time->tv_sec = seconds; - unix_time->tv_nsec = (nt_time % 10000000) * 100; - } - return (seconds); + unix_time->tv_sec = seconds; + unix_time->tv_nsec = (nt_time % 10000000) * 100; +} + +/* + * smb_time_gmt_to_local, smb_time_local_to_gmt + * + * Apply the gmt offset to convert between local time and gmt + */ +int32_t +smb_time_gmt_to_local(smb_request_t *sr, int32_t gmt) +{ + if ((gmt == 0) || (gmt == -1)) + return (0); + + return (gmt - sr->sr_gmtoff); } +int32_t +smb_time_local_to_gmt(smb_request_t *sr, int32_t local) +{ + if ((local == 0) || (local == -1)) + return (0); + + return (local + sr->sr_gmtoff); +} + + /* - * smb_dos_to_ux_time + * smb_time_dos_to_unix * * Convert SMB_DATE & SMB_TIME values to a unix timestamp. * @@ -1650,7 +1682,7 @@ nt_to_unix_time(uint64_t nt_time, timestruc_t *unix_time) * so that the caller can identify and handle this special case. */ int32_t -smb_dos_to_ux_time(int16_t date, int16_t time) +smb_time_dos_to_unix(int16_t date, int16_t time) { struct tm atm; @@ -1669,13 +1701,19 @@ smb_dos_to_ux_time(int16_t date, int16_t time) return (smb_timegm(&atm)); } -int32_t -smb_ux_to_dos_time(int32_t ux_time, int16_t *date_p, int16_t *time_p) +void +smb_time_unix_to_dos(int32_t ux_time, int16_t *date_p, int16_t *time_p) { struct tm atm; int i; time_t tmp_time; + if (ux_time == 0) { + *date_p = 0; + *time_p = 0; + return; + } + tmp_time = (time_t)ux_time; (void) smb_gmtime_r(&tmp_time, &atm); @@ -1699,7 +1737,6 @@ smb_ux_to_dos_time(int32_t ux_time, int16_t *date_p, int16_t *time_p) *time_p = (short)i; } - return (ux_time); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c index 9cda0fe7cf..386e02af4b 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_vops.c +++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c @@ -890,13 +890,15 @@ smb_vop_setup_xvattr(smb_attr_t *smb_attr, xvattr_t *xvattr) * 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, cred_t *cr) + void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr) { int error = 0; - int rdirent_flags = 0; + int flags = 0; int rdirent_size; struct uio auio; struct iovec aiov; @@ -905,7 +907,7 @@ smb_vop_readdir(vnode_t *vp, uint32_t offset, return (ENOTDIR); if (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) { - rdirent_flags = V_RDDIR_ENTFLAGS; + flags |= V_RDDIR_ENTFLAGS; rdirent_size = sizeof (edirent_t); } else { rdirent_size = sizeof (dirent64_t); @@ -914,6 +916,9 @@ smb_vop_readdir(vnode_t *vp, uint32_t offset, 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; @@ -924,7 +929,7 @@ smb_vop_readdir(vnode_t *vp, uint32_t offset, auio.uio_fmode = 0; (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); - error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, rdirent_flags); + error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags); VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct); if (error == 0) diff --git a/usr/src/uts/common/fs/smbsrv/smb_write_raw.c b/usr/src/uts/common/fs/smbsrv/smb_write_raw.c index 1872c9ef46..a1d7948412 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_write_raw.c +++ b/usr/src/uts/common/fs/smbsrv/smb_write_raw.c @@ -349,11 +349,10 @@ smb_com_write_raw(struct smb_request *sr) /* * smb_write_raw_helper will call smb_opipe_write or - * smb_fsop_write as appropriate, handle the NODE_FLAGS_SET_SIZE - * flag (if set) and update the other f_node fields. It's possible - * that data_length may be 0 for this transfer but we still want - * process it since it will update the file state (seek position, - * file size (possibly), etc). + * smb_fsop_write as appropriate, and update the other f_node fields. + * It's possible that data_length may be 0 for this transfer but + * we still want to process it since it will update the file state + * (seek position, file size (possibly), etc). */ rc = smb_write_raw_helper(sr, &uio, stability, &off, &lcount); @@ -475,11 +474,11 @@ notify_write_raw_complete: /* * smb_write_raw_helper * - * This function will call smb_opipe_write or smb_fsop_write as appropriate, - * handle the NODE_FLAGS_SET_SIZE flag (if set) and update the other f_node - * fields. It's possible that data_length may be 0 for this transfer but - * we still want process it since it will update the file state (seek - * position, file size (possibly), etc). + * This function will call smb_opipe_write or smb_fsop_write + * as appropriate, and update the other f_node fields. + * It's possible that data_length may be 0 for this transfer but + * we still want process it since it will update the file state + * (seek position, file size (possibly), etc). * * Returns 0 for success, non-zero for failure */ |
