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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 #include <smbsrv/smb_kproto.h> 28 #include <smbsrv/smb_fsops.h> 29 #include <smbsrv/smb_share.h> 30 #include <smbsrv/string.h> 31 #include <sys/fs/zfs.h> 32 #include <smbsrv/smb_xdr.h> 33 #include <smbsrv/smb_door.h> 34 #include <smbsrv/smb_idmap.h> 35 36 /* 37 * A user/group quota entry passed over the wire consists of: 38 * - next offset (uint32_t) 39 * - length of SID (uint32_t) 40 * - last modified time (uint64_t) 41 * - quota used (uint64_t) 42 * - quota limit (uint64_t) 43 * - quota threahold (uint64_t) 44 * - variable length sid - max = 32 bytes 45 * SMB_QUOTA_SIZE_NO_SID is the size of the above, excluding the sid. 46 */ 47 #define SMB_QUOTA_SIZE_NO_SID \ 48 ((2 * sizeof (uint32_t)) + (4 * sizeof (uint64_t))) 49 #define SMB_QUOTA_EST_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_EST_SID_SIZE) 50 #define SMB_QUOTA_MAX_SIZE (SMB_QUOTA_SIZE_NO_SID + SMB_MAX_SID_SIZE) 51 52 53 /* 54 * smb_quota_init_sids 55 * 56 * If the query is of type SMB_QUOTA_QUERY_SIDLIST or 57 * SMB_QUOTA_QUERY_STARTSID decode the list of sids from 58 * the client request into request->qq_sid_list. 59 * Otherwise (type SMB_QUOTA_QUERY_ALL) find the resume sid 60 * and insert it into request->qq_sid_list, or reset the 61 * resume sid to NULL if request->qq_restart. 62 * 63 * Returns: NT_STATUS codes 64 */ 65 uint32_t 66 smb_quota_init_sids(mbuf_chain_t *mbc, smb_quota_query_t *request, 67 smb_ofile_t *ofile) 68 { 69 smb_quota_sid_t *sid; 70 list_t *sid_list; 71 uint32_t status = NT_STATUS_SUCCESS; 72 73 sid_list = &request->qq_sid_list; 74 list_create(sid_list, sizeof (smb_quota_sid_t), 75 offsetof(smb_quota_sid_t, qs_list_node)); 76 77 switch (request->qq_query_op) { 78 case SMB_QUOTA_QUERY_SIDLIST: 79 case SMB_QUOTA_QUERY_STARTSID: 80 status = smb_quota_decode_sids(mbc, sid_list); 81 break; 82 case SMB_QUOTA_QUERY_ALL: 83 if (request->qq_restart) 84 smb_ofile_set_quota_resume(ofile, NULL); 85 else { 86 sid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP); 87 list_insert_tail(sid_list, sid); 88 smb_ofile_get_quota_resume(ofile, sid->qs_sidstr, 89 SMB_SID_STRSZ); 90 if (*sid->qs_sidstr == '\0') 91 status = NT_STATUS_INVALID_PARAMETER; 92 } 93 break; 94 default: 95 status = NT_STATUS_INVALID_PARAMETER; 96 break; 97 } 98 99 return (status); 100 } 101 102 /* 103 * smb_quota_free_sids 104 */ 105 void 106 smb_quota_free_sids(smb_quota_query_t *request) 107 { 108 list_t *sid_list; 109 smb_quota_sid_t *sid; 110 111 sid_list = &request->qq_sid_list; 112 113 while ((sid = list_head(sid_list)) != NULL) { 114 list_remove(sid_list, sid); 115 kmem_free(sid, sizeof (smb_quota_sid_t)); 116 } 117 118 list_destroy(sid_list); 119 } 120 121 /* 122 * smb_quota_decode_sids 123 * 124 * Decode the SIDs from the data block and stores them in string form in list. 125 * Eaxh sid entry comprises: 126 * next_offset (4 bytes) - offset of next entry 127 * sid length (4 bytes) 128 * sid (variable length = sidlen) 129 * The last entry will have a next_offset value of 0. 130 * 131 * Returns NT_STATUS codes. 132 */ 133 uint32_t 134 smb_quota_decode_sids(mbuf_chain_t *mbc, list_t *list) 135 { 136 uint32_t offset, mb_offset, sid_offset, bytes_left; 137 uint32_t next_offset, sidlen; 138 smb_sid_t *sid; 139 smb_quota_sid_t *qsid; 140 uint32_t status = NT_STATUS_SUCCESS; 141 struct mbuf_chain sidbuf; 142 int rc; 143 144 offset = 0; 145 do { 146 mb_offset = offset + mbc->chain_offset; 147 bytes_left = mbc->max_bytes - mb_offset; 148 rc = MBC_SHADOW_CHAIN(&sidbuf, mbc, 149 mb_offset, bytes_left); 150 if (rc != 0) { 151 status = NT_STATUS_INVALID_PARAMETER; 152 break; 153 } 154 155 if (smb_mbc_decodef(&sidbuf, "ll", &next_offset, &sidlen)) { 156 status = NT_STATUS_INVALID_PARAMETER; 157 break; 158 } 159 160 sid_offset = offset + (2 * sizeof (uint32_t)); 161 sid = smb_decode_sid(mbc, sid_offset); 162 if (sid == NULL) { 163 status = NT_STATUS_INVALID_PARAMETER; 164 break; 165 } 166 167 qsid = kmem_zalloc(sizeof (smb_quota_sid_t), KM_SLEEP); 168 smb_sid_tostr(sid, qsid->qs_sidstr); 169 smb_sid_free(sid); 170 sid = NULL; 171 172 list_insert_tail(list, qsid); 173 offset += next_offset; 174 } while ((next_offset != 0) && (bytes_left > 0)); 175 176 return (status); 177 } 178 179 /* 180 * smb_quota_max_quota 181 * 182 * If the query is if type SMB_QUOTA_QUERY_SIDLIST a quota entry 183 * is returned for each sid in the sidlist. request->qr_max_quota 184 * is set to 0 and is unused. 185 * Otherwise (for SMB_QUOTA_QUERY_STARTSID and SMB_QUOTA_QUERY_ALL) 186 * max_quota is the maximum number of quota entries requested from 187 * the file system (via door call smb_quota_query()). 188 * If single is set max_quota is set to 1. If single is not set 189 * max quota is calculated as the number of quotas of size 190 * SMB_QUOTA_EST_SIZE that would fit in the response buffer. 191 */ 192 void 193 smb_quota_max_quota(mbuf_chain_t *mbc, smb_quota_query_t *request) 194 { 195 if (request->qq_query_op == SMB_QUOTA_QUERY_SIDLIST) 196 request->qq_max_quota = 0; 197 else if (request->qq_single) 198 request->qq_max_quota = 1; 199 else 200 request->qq_max_quota = (mbc->max_bytes / SMB_QUOTA_EST_SIZE); 201 } 202 203 /* 204 * smb_quota_decode_quotas 205 * 206 * Decode the quota entries into a list_t of smb_quota_t. 207 * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry, 208 * excluding the sid. 209 * The last entry will have a next_offset value of 0. 210 * 211 * Returns NT_STATUS codes. 212 */ 213 uint32_t 214 smb_quota_decode_quotas(mbuf_chain_t *mbc, list_t *list) 215 { 216 uint32_t offset, mb_offset, sid_offset, bytes_left; 217 uint32_t next_offset, sidlen; 218 uint64_t mtime; 219 smb_sid_t *sid; 220 smb_quota_t *quota; 221 uint32_t status = NT_STATUS_SUCCESS; 222 struct mbuf_chain quotabuf; 223 int rc; 224 225 offset = 0; 226 do { 227 mb_offset = offset + mbc->chain_offset; 228 bytes_left = mbc->max_bytes - mb_offset; 229 rc = MBC_SHADOW_CHAIN("abuf, mbc, 230 mb_offset, bytes_left); 231 if (rc != 0) { 232 status = NT_STATUS_INVALID_PARAMETER; 233 break; 234 } 235 236 quota = kmem_zalloc(sizeof (smb_quota_t), KM_SLEEP); 237 238 if (smb_mbc_decodef("abuf, "llqqqq", 239 &next_offset, &sidlen, &mtime, 240 "a->q_used, "a->q_thresh, "a->q_limit)) { 241 kmem_free(quota, sizeof (smb_quota_t)); 242 status = NT_STATUS_INVALID_PARAMETER; 243 break; 244 } 245 246 sid_offset = offset + SMB_QUOTA_SIZE_NO_SID; 247 sid = smb_decode_sid(mbc, sid_offset); 248 if (sid == NULL) { 249 kmem_free(quota, sizeof (smb_quota_t)); 250 status = NT_STATUS_INVALID_PARAMETER; 251 break; 252 } 253 254 bzero(quota->q_sidstr, SMB_SID_STRSZ); 255 smb_sid_tostr(sid, quota->q_sidstr); 256 smb_sid_free(sid); 257 sid = NULL; 258 259 list_insert_tail(list, quota); 260 offset += next_offset; 261 } while ((next_offset != 0) && (bytes_left > 0)); 262 263 return (status); 264 } 265 266 /* 267 * smb_quota_free_quotas 268 */ 269 void 270 smb_quota_free_quotas(list_t *list) 271 { 272 smb_quota_t *quota; 273 274 while ((quota = list_head(list)) != NULL) { 275 list_remove(list, quota); 276 kmem_free(quota, sizeof (smb_quota_t)); 277 } 278 279 list_destroy(list); 280 } 281 282 /* 283 * smb_quota_encode_quotas 284 * 285 * Encode the quota entries from a list_t of smb_quota_t. 286 * SMB_QUOTA_SIZE_NO_SID is the size of a quota entry, 287 * excluding the sid. 288 * The last entry will have a next_offset value of 0. 289 * Sets the last encoded SID as the resume sid. 290 */ 291 uint32_t 292 smb_quota_encode_quotas(mbuf_chain_t *mbc, smb_quota_query_t *request, 293 smb_quota_response_t *reply, smb_ofile_t *ofile) 294 { 295 uint32_t next_offset, sid_offset; 296 uint64_t mtime = 0; 297 uint32_t sidlen, pad; 298 smb_sid_t *sid; 299 char *sidstr = NULL, *resume = NULL; 300 smb_quota_t *quota, *next_quota; 301 list_t *list = &reply->qr_quota_list; 302 303 int rc; 304 uint32_t status = NT_STATUS_SUCCESS; 305 306 quota = list_head(list); 307 while (quota) { 308 next_quota = list_next(list, quota); 309 sidstr = quota->q_sidstr; 310 if ((sid = smb_sid_fromstr(sidstr)) == NULL) { 311 quota = next_quota; 312 continue; 313 } 314 315 sidlen = smb_sid_len(sid); 316 sid_offset = SMB_QUOTA_SIZE_NO_SID; 317 next_offset = sid_offset + sidlen; 318 pad = smb_pad_align(next_offset, 8); 319 next_offset += pad; 320 321 if (!MBC_ROOM_FOR(mbc, next_offset)) { 322 smb_sid_free(sid); 323 break; 324 } 325 if (!MBC_ROOM_FOR(mbc, 326 next_offset + SMB_QUOTA_MAX_SIZE)) { 327 next_quota = NULL; 328 } 329 330 rc = smb_mbc_encodef(mbc, "llqqqq", 331 next_quota ? next_offset : 0, sidlen, mtime, 332 quota->q_used, quota->q_thresh, quota->q_limit); 333 if (rc == 0) { 334 smb_encode_sid(mbc, sid); 335 rc = smb_mbc_encodef(mbc, "#.", pad); 336 } 337 338 smb_sid_free(sid); 339 340 if (rc != 0) { 341 status = NT_STATUS_INTERNAL_ERROR; 342 break; 343 } 344 345 resume = sidstr; 346 quota = next_quota; 347 } 348 349 if ((status == NT_STATUS_SUCCESS) && 350 ((request->qq_query_op == SMB_QUOTA_QUERY_STARTSID) || 351 (request->qq_query_op == SMB_QUOTA_QUERY_ALL))) { 352 smb_ofile_set_quota_resume(ofile, resume); 353 } 354 355 return (status); 356 } 357 358 /* 359 * smb_quota_query_user_quota 360 * 361 * Get user quota information for a single user (uid) 362 * for the current file system. 363 * Find the user's sid, insert it in the sidlist of a 364 * smb_quota_query_t request and invoke the door call 365 * smb_quota_query() to obtain the quota information. 366 * 367 * Returns: NT_STATUS codes. 368 */ 369 uint32_t 370 smb_quota_query_user_quota(smb_request_t *sr, uid_t uid, smb_quota_t *quota) 371 { 372 smb_sid_t *sid; 373 smb_quota_sid_t qsid; 374 smb_quota_query_t request; 375 smb_quota_response_t reply; 376 list_t *sid_list; 377 smb_quota_t *q; 378 smb_node_t *tnode; 379 uint32_t status = NT_STATUS_SUCCESS; 380 381 if (smb_idmap_getsid(uid, SMB_IDMAP_USER, &sid) != IDMAP_SUCCESS) 382 return (NT_STATUS_INTERNAL_ERROR); 383 384 smb_sid_tostr(sid, qsid.qs_sidstr); 385 smb_sid_free(sid); 386 387 bzero(&request, sizeof (smb_quota_query_t)); 388 bzero(&reply, sizeof (smb_quota_response_t)); 389 390 tnode = sr->tid_tree->t_snode; 391 request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 392 if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) { 393 kmem_free(request.qq_root_path, MAXPATHLEN); 394 return (NT_STATUS_INTERNAL_ERROR); 395 } 396 397 sid_list = &request.qq_sid_list; 398 list_create(sid_list, sizeof (smb_quota_sid_t), 399 offsetof(smb_quota_sid_t, qs_list_node)); 400 list_insert_tail(sid_list, &qsid); 401 402 request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST; 403 request.qq_single = B_TRUE; 404 405 if (smb_quota_query(sr->sr_server, &request, &reply) != 0) { 406 status = NT_STATUS_INTERNAL_ERROR; 407 } else { 408 if (reply.qr_status != NT_STATUS_SUCCESS) { 409 status = reply.qr_status; 410 } else { 411 q = list_head(&reply.qr_quota_list); 412 if ((q == NULL) || 413 (strcmp(qsid.qs_sidstr, q->q_sidstr) != 0)) { 414 /* should never happen */ 415 status = NT_STATUS_INTERNAL_ERROR; 416 } else { 417 bcopy(q, quota, sizeof (smb_quota_t)); 418 } 419 } 420 xdr_free(smb_quota_response_xdr, (char *)&reply); 421 } 422 423 kmem_free(request.qq_root_path, MAXPATHLEN); 424 list_remove(sid_list, &qsid); 425 list_destroy(sid_list); 426 427 return (status); 428 } 429 430 /* 431 * smb_quota_query 432 * 433 * Door call to query quotas for the provided filesystem path. 434 * Returns: -1 - door call (or encode/decode) failure. 435 * 0 - success. Status set in reply. 436 */ 437 int 438 smb_quota_query(smb_server_t *sv, smb_quota_query_t *request, 439 smb_quota_response_t *reply) 440 { 441 int rc; 442 443 rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_QUERY, 444 request, smb_quota_query_xdr, reply, smb_quota_response_xdr); 445 446 return (rc); 447 } 448 449 /* 450 * smb_quota_set 451 * 452 * Door call to set quotas for the provided filesystem path. 453 * Returns: -1 - door call (or encode/decode) failure. 454 * 0 - success. Status set in reply. 455 */ 456 int 457 smb_quota_set(smb_server_t *sv, smb_quota_set_t *request, uint32_t *reply) 458 { 459 int rc; 460 461 rc = smb_kdoor_upcall(sv, SMB_DR_QUOTA_SET, 462 request, smb_quota_set_xdr, reply, xdr_uint32_t); 463 464 return (rc); 465 } 466