1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <smbsrv/smb_kproto.h> 27 #include <smbsrv/smb_fsops.h> 28 #include <smbsrv/smbinfo.h> 29 30 static int smb_trans2_set_fs_ctrl_info(smb_request_t *, smb_xa_t *); 31 32 /* 33 * smb_com_query_information_disk 34 * 35 * The SMB_COM_QUERY_INFORMATION_DISK command is used to determine the 36 * capacity and remaining free space on the drive hosting the directory 37 * structure indicated by Tid in the SMB header. 38 * 39 * The blocking/allocation units used in this response may be independent 40 * of the actual physical or logical blocking/allocation algorithm(s) used 41 * internally by the server. However, they must accurately reflect the 42 * amount of space on the server. 43 * 44 * This SMB only returns 16 bits of information for each field, which may 45 * not be large enough for some disk systems. In particular TotalUnits is 46 * commonly > 64K. Fortunately, it turns out the all the client cares 47 * about is the total disk size, in bytes, and the free space, in bytes. 48 * So, it is reasonable for a server to adjust the relative values of 49 * BlocksPerUnit and BlockSize to accommodate. If after all adjustment, 50 * the numbers are still too high, the largest possible values for 51 * TotalUnit or FreeUnits (i.e. 0xFFFF) should be returned. 52 */ 53 54 smb_sdrc_t 55 smb_pre_query_information_disk(smb_request_t *sr) 56 { 57 DTRACE_SMB_1(op__QueryInformationDisk__start, smb_request_t *, sr); 58 return (SDRC_SUCCESS); 59 } 60 61 void 62 smb_post_query_information_disk(smb_request_t *sr) 63 { 64 DTRACE_SMB_1(op__QueryInformationDisk__done, smb_request_t *, sr); 65 } 66 67 smb_sdrc_t 68 smb_com_query_information_disk(smb_request_t *sr) 69 { 70 int rc; 71 fsblkcnt64_t total_blocks, free_blocks; 72 unsigned long block_size, unit_size; 73 unsigned short blocks_per_unit, bytes_per_block; 74 unsigned short total_units, free_units; 75 smb_fssize_t fssize; 76 77 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { 78 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); 79 return (SDRC_ERROR); 80 } 81 82 if (smb_fssize(sr, &fssize) != 0) 83 return (SDRC_ERROR); 84 85 unit_size = fssize.fs_sectors_per_unit; 86 block_size = fssize.fs_bytes_per_sector; 87 total_blocks = fssize.fs_caller_units; 88 free_blocks = fssize.fs_caller_avail; 89 90 /* 91 * It seems that DOS clients cannot handle block sizes 92 * bigger than 512 KB. So we have to set the block size at 93 * most to 512 94 */ 95 while (block_size > 512) { 96 block_size >>= 1; 97 unit_size <<= 1; 98 } 99 100 /* adjust blocks and sizes until they fit into a word */ 101 while (total_blocks >= 0xFFFF) { 102 total_blocks >>= 1; 103 free_blocks >>= 1; 104 if ((unit_size <<= 1) > 0xFFFF) { 105 unit_size >>= 1; 106 total_blocks = 0xFFFF; 107 free_blocks <<= 1; 108 break; 109 } 110 } 111 112 total_units = (total_blocks >= 0xFFFF) ? 113 0xFFFF : (unsigned short)total_blocks; 114 free_units = (free_blocks >= 0xFFFF) ? 115 0xFFFF : (unsigned short)free_blocks; 116 bytes_per_block = (unsigned short)block_size; 117 blocks_per_unit = (unsigned short)unit_size; 118 119 rc = smbsr_encode_result(sr, 5, 0, "bwwww2.w", 120 5, 121 total_units, /* total_units */ 122 blocks_per_unit, /* blocks_per_unit */ 123 bytes_per_block, /* blocksize */ 124 free_units, /* free_units */ 125 0); /* bcc */ 126 127 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 128 } 129 130 /* 131 * smb_com_trans2_query_fs_information 132 * 133 * This transaction requests information about the filesystem. 134 * The following information levels are supported: 135 * 136 * InformationLevel Value 137 * ================================== ====== 138 * SMB_INFO_ALLOCATION 1 139 * SMB_INFO_VOLUME 2 140 * SMB_QUERY_FS_VOLUME_INFO 0x102 141 * SMB_QUERY_FS_SIZE_INFO 0x103 142 * SMB_QUERY_FS_DEVICE_INFO 0x104 143 * SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 144 * SMB_FILE_FS_VOLUME_INFORMATION 1001 145 * SMB_FILE_FS_SIZE_INFORMATION 1003 146 * SMB_FILE_FS_DEVICE_INFORMATION 1004 147 * SMB_FILE_FS_ATTRIBUTE_INFORMATION 1005 148 * SMB_FILE_FS_CONTROL_INFORMATION 1006 149 * SMB_FILE_FS_FULLSIZE_INFORMATION 1007 150 * 151 * The fsid provides a system-wide unique file system ID. 152 * fsid.val[0] is the 32-bit dev for the file system of the share root 153 * smb_node. 154 * fsid.val[1] is the file system type. 155 */ 156 smb_sdrc_t 157 smb_com_trans2_query_fs_information(smb_request_t *sr, smb_xa_t *xa) 158 { 159 uint32_t flags; 160 char *encode_str, *tmpbuf; 161 uint64_t max_int; 162 uint16_t infolev; 163 int rc, length, buflen; 164 smb_tree_t *tree; 165 smb_node_t *snode; 166 char *fsname = "NTFS"; 167 fsid_t fsid; 168 smb_fssize_t fssize; 169 smb_msgbuf_t mb; 170 171 tree = sr->tid_tree; 172 173 if (!STYPE_ISDSK(tree->t_res_type)) { 174 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 175 ERRDOS, ERROR_ACCESS_DENIED); 176 return (SDRC_ERROR); 177 } 178 179 if (smb_mbc_decodef(&xa->req_param_mb, "w", &infolev) != 0) 180 return (SDRC_ERROR); 181 182 snode = tree->t_snode; 183 fsid = SMB_NODE_FSID(snode); 184 185 switch (infolev) { 186 case SMB_INFO_ALLOCATION: 187 if (smb_fssize(sr, &fssize) != 0) 188 return (SDRC_ERROR); 189 190 max_int = (uint64_t)UINT_MAX; 191 if (fssize.fs_caller_units > max_int) 192 fssize.fs_caller_units = max_int; 193 if (fssize.fs_caller_avail > max_int) 194 fssize.fs_caller_avail = max_int; 195 196 (void) smb_mbc_encodef(&xa->rep_data_mb, "llllw", 197 0, 198 fssize.fs_sectors_per_unit, 199 fssize.fs_caller_units, 200 fssize.fs_caller_avail, 201 fssize.fs_bytes_per_sector); 202 break; 203 204 case SMB_INFO_VOLUME: 205 /* 206 * In this response, the unicode volume label is NOT 207 * expected to be aligned. Encode ('U') into a temporary 208 * buffer, then encode buffer as a byte stream ('#c'). 209 */ 210 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) || 211 (sr->session->native_os == NATIVE_OS_WIN95)) { 212 length = smb_wcequiv_strlen(tree->t_volume); 213 buflen = length + sizeof (smb_wchar_t); 214 tmpbuf = smb_srm_zalloc(sr, buflen); 215 smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen, 216 SMB_MSGBUF_UNICODE); 217 rc = smb_msgbuf_encode(&mb, "U", tree->t_volume); 218 if (rc >= 0) { 219 rc = smb_mbc_encodef(&xa->rep_data_mb, 220 "%lb#c", sr, fsid.val[0], 221 length, length, tmpbuf); 222 } 223 smb_msgbuf_term(&mb); 224 } else { 225 length = strlen(tree->t_volume); 226 rc = smb_mbc_encodef(&xa->rep_data_mb, "%lbs", sr, 227 fsid.val[0], length, tree->t_volume); 228 } 229 230 if (rc < 0) 231 return (SDRC_ERROR); 232 break; 233 234 case SMB_QUERY_FS_VOLUME_INFO: 235 case SMB_FILE_FS_VOLUME_INFORMATION: 236 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) || 237 (sr->session->native_os == NATIVE_OS_WIN95)) { 238 length = smb_wcequiv_strlen(tree->t_volume); 239 encode_str = "%qllb.U"; 240 } else { 241 length = strlen(tree->t_volume); 242 encode_str = "%qllb.s"; 243 } 244 245 /* 246 * NT has the "supports objects" flag set to 1. 247 */ 248 (void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr, 249 0ll, /* Volume creation time */ 250 fsid.val[0], /* Volume serial number */ 251 length, /* label length */ 252 0, /* Supports objects */ 253 tree->t_volume); 254 break; 255 256 case SMB_QUERY_FS_SIZE_INFO: 257 case SMB_FILE_FS_SIZE_INFORMATION: 258 if (smb_fssize(sr, &fssize) != 0) 259 return (SDRC_ERROR); 260 261 (void) smb_mbc_encodef(&xa->rep_data_mb, "qqll", 262 fssize.fs_caller_units, 263 fssize.fs_caller_avail, 264 fssize.fs_sectors_per_unit, 265 fssize.fs_bytes_per_sector); 266 break; 267 268 case SMB_QUERY_FS_DEVICE_INFO: 269 case SMB_FILE_FS_DEVICE_INFORMATION: 270 (void) smb_mbc_encodef(&xa->rep_data_mb, "ll", 271 FILE_DEVICE_FILE_SYSTEM, 272 FILE_DEVICE_IS_MOUNTED); 273 break; 274 275 case SMB_QUERY_FS_ATTRIBUTE_INFO: 276 case SMB_FILE_FS_ATTRIBUTE_INFORMATION: 277 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) || 278 (sr->session->native_os == NATIVE_OS_WINNT) || 279 (sr->session->native_os == NATIVE_OS_WIN2000) || 280 (sr->session->native_os == NATIVE_OS_WIN95) || 281 (sr->session->native_os == NATIVE_OS_MACOS)) { 282 length = smb_wcequiv_strlen(fsname); 283 encode_str = "%lllU"; 284 sr->smb_flg2 |= SMB_FLAGS2_UNICODE; 285 } else { 286 length = strlen(fsname); 287 encode_str = "%llls"; 288 } 289 290 flags = FILE_CASE_PRESERVED_NAMES; 291 292 if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK) 293 flags |= FILE_UNICODE_ON_DISK; 294 295 if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS) 296 flags |= FILE_PERSISTENT_ACLS; 297 298 if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0) 299 flags |= FILE_CASE_SENSITIVE_SEARCH; 300 301 if (tree->t_flags & SMB_TREE_STREAMS) 302 flags |= FILE_NAMED_STREAMS; 303 304 if (tree->t_flags & SMB_TREE_QUOTA) 305 flags |= FILE_VOLUME_QUOTAS; 306 307 if (tree->t_flags & SMB_TREE_SPARSE) 308 flags |= FILE_SUPPORTS_SPARSE_FILES; 309 310 (void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr, 311 flags, 312 MAXNAMELEN, /* max name */ 313 length, /* label length */ 314 fsname); 315 break; 316 317 case SMB_FILE_FS_CONTROL_INFORMATION: 318 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) { 319 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 320 ERRDOS, ERROR_NOT_SUPPORTED); 321 return (SDRC_ERROR); 322 } 323 324 (void) smb_mbc_encodef(&xa->rep_data_mb, "qqqqqll", 325 0, /* free space start filtering - MUST be 0 */ 326 0, /* free space threshold - MUST be 0 */ 327 0, /* free space stop filtering - MUST be 0 */ 328 SMB_QUOTA_UNLIMITED, /* default quota threshold */ 329 SMB_QUOTA_UNLIMITED, /* default quota limit */ 330 FILE_VC_QUOTA_ENFORCE, /* fs control flag */ 331 0); /* pad bytes */ 332 break; 333 334 case SMB_FILE_FS_FULLSIZE_INFORMATION: 335 if (smb_fssize(sr, &fssize) != 0) 336 return (SDRC_ERROR); 337 338 (void) smb_mbc_encodef(&xa->rep_data_mb, "qqqll", 339 fssize.fs_caller_units, 340 fssize.fs_caller_avail, 341 fssize.fs_volume_avail, 342 fssize.fs_sectors_per_unit, 343 fssize.fs_bytes_per_sector); 344 break; 345 346 case SMB_FILE_FS_LABEL_INFORMATION: 347 case SMB_FILE_FS_OBJECTID_INFORMATION: 348 case SMB_FILE_FS_DRIVERPATH_INFORMATION: 349 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 350 ERRDOS, ERROR_NOT_SUPPORTED); 351 return (SDRC_ERROR); 352 353 default: 354 smbsr_error(sr, NT_STATUS_INVALID_LEVEL, 355 ERRDOS, ERROR_INVALID_LEVEL); 356 return (SDRC_ERROR); 357 } 358 359 return (SDRC_SUCCESS); 360 } 361 362 /* 363 * smb_fssize 364 * 365 * File system size information, for the volume and for the user 366 * initiating the request. 367 * 368 * If there's no quota entry for the user initiating the request, 369 * caller_units and caller_avail are the total and available units 370 * for the volume (volume_units, volume_avail). 371 * If there is a quota entry for the user initiating the request, 372 * and it is not SMB_QUOTA_UNLIMITED, calculate caller_units and 373 * caller_avail as follows: 374 * caller_units = quota limit / bytes_per_unit 375 * caller_avail = remaining quota / bytes_per_unit 376 * 377 * A quota limit of SMB_QUOTA_UNLIMITED means that the user's quota 378 * is specfied as unlimited. A quota limit of 0 means there is no 379 * quota specified for the user. 380 * 381 * Returns: 0 (success) or an errno value 382 */ 383 int 384 smb_fssize(smb_request_t *sr, smb_fssize_t *fssize) 385 { 386 smb_node_t *node; 387 struct statvfs64 df; 388 uid_t uid; 389 smb_quota_t quota; 390 int spu; /* sectors per unit */ 391 int rc; 392 393 bzero(fssize, sizeof (smb_fssize_t)); 394 node = sr->tid_tree->t_snode; 395 if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0) 396 return (rc); 397 398 if (df.f_frsize < DEV_BSIZE) 399 df.f_frsize = DEV_BSIZE; 400 if (df.f_bsize < df.f_frsize) 401 df.f_bsize = df.f_frsize; 402 spu = df.f_bsize / df.f_frsize; 403 404 fssize->fs_bytes_per_sector = (uint16_t)df.f_frsize; 405 fssize->fs_sectors_per_unit = spu; 406 407 if (df.f_bavail > df.f_blocks) 408 df.f_bavail = 0; 409 410 fssize->fs_volume_units = df.f_blocks / spu; 411 fssize->fs_volume_avail = df.f_bavail / spu; 412 fssize->fs_caller_units = df.f_blocks / spu; 413 fssize->fs_caller_avail = df.f_bavail / spu; 414 415 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) 416 return (0); 417 418 uid = crgetuid(sr->uid_user->u_cred); 419 if (smb_quota_query_user_quota(sr, uid, "a) != NT_STATUS_SUCCESS) 420 return (0); 421 422 if ((quota.q_limit != SMB_QUOTA_UNLIMITED) && (quota.q_limit != 0)) { 423 fssize->fs_caller_units = quota.q_limit / df.f_bsize; 424 if (quota.q_limit <= quota.q_used) 425 fssize->fs_caller_avail = 0; 426 else 427 fssize->fs_caller_avail = 428 (quota.q_limit - quota.q_used) / df.f_bsize; 429 } 430 431 return (0); 432 } 433 434 /* 435 * smb_com_trans2_set_fs_information 436 * 437 * This transaction sets filesystem information. 438 * The following information levels are supported: 439 * 440 * InformationLevel Value 441 * ================================== ====== 442 * SMB_FILE_FS_CONTROL_INFORMATION 1006 443 */ 444 smb_sdrc_t 445 smb_com_trans2_set_fs_information(smb_request_t *sr, smb_xa_t *xa) 446 { 447 smb_tree_t *tree; 448 uint16_t infolev; 449 450 tree = sr->tid_tree; 451 if (!STYPE_ISDSK(tree->t_res_type)) { 452 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 453 ERRDOS, ERROR_ACCESS_DENIED); 454 return (SDRC_ERROR); 455 } 456 457 if (smb_mbc_decodef(&xa->req_param_mb, "ww", 458 &sr->smb_fid, &infolev) != 0) 459 return (SDRC_ERROR); 460 461 switch (infolev) { 462 case SMB_FILE_FS_CONTROL_INFORMATION: 463 if (smb_trans2_set_fs_ctrl_info(sr, xa) != 0) 464 return (SDRC_ERROR); 465 break; 466 467 case SMB_FILE_FS_VOLUME_INFORMATION: 468 case SMB_FILE_FS_LABEL_INFORMATION: 469 case SMB_FILE_FS_SIZE_INFORMATION: 470 case SMB_FILE_FS_DEVICE_INFORMATION: 471 case SMB_FILE_FS_ATTRIBUTE_INFORMATION: 472 case SMB_FILE_FS_FULLSIZE_INFORMATION: 473 case SMB_FILE_FS_OBJECTID_INFORMATION: 474 case SMB_FILE_FS_DRIVERPATH_INFORMATION: 475 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 476 ERRDOS, ERROR_NOT_SUPPORTED); 477 return (SDRC_ERROR); 478 479 default: 480 smbsr_error(sr, NT_STATUS_INVALID_LEVEL, 481 ERRDOS, ERROR_INVALID_LEVEL); 482 return (SDRC_ERROR); 483 } 484 485 return (SDRC_SUCCESS); 486 } 487 488 /* 489 * smb_trans2_set_fs_ctrl_info 490 * 491 * Only users with Admin privileges (i.e. of the BUILTIN/Administrators 492 * group) will be allowed to set quotas. 493 * 494 * Currently QUOTAS are always ENFORCED and the default values 495 * are always SMB_QUOTA_UNLIMITED (none). Any attempt to set 496 * values other than these will result in NT_STATUS_NOT_SUPPORTED. 497 */ 498 static int 499 smb_trans2_set_fs_ctrl_info(smb_request_t *sr, smb_xa_t *xa) 500 { 501 int rc; 502 uint64_t fstart, fthresh, fstop, qthresh, qlimit; 503 uint32_t qctrl, qpad; 504 505 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) { 506 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 507 ERRDOS, ERROR_NOT_SUPPORTED); 508 return (-1); 509 } 510 511 if (!smb_user_is_admin(sr->uid_user)) { 512 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 513 ERRDOS, ERROR_ACCESS_DENIED); 514 return (-1); 515 } 516 517 rc = smb_mbc_decodef(&xa->req_data_mb, "qqqqqll", &fstart, 518 &fthresh, &fstop, &qthresh, &qlimit, &qctrl, &qpad); 519 520 if ((rc != 0) || (fstart != 0) || (fthresh != 0) || (fstop != 0)) { 521 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 522 ERRDOS, ERROR_INVALID_PARAMETER); 523 return (-1); 524 } 525 526 /* Only support ENFORCED quotas with UNLIMITED default */ 527 if ((qctrl != FILE_VC_QUOTA_ENFORCE) || 528 (qlimit != SMB_QUOTA_UNLIMITED) || 529 (qthresh != SMB_QUOTA_UNLIMITED)) { 530 smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 531 ERRDOS, ERROR_NOT_SUPPORTED); 532 return (-1); 533 } 534 535 return (0); 536 } 537