1 /* 2 * Copyright (c) 2000-2001, Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 * 39 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/time.h> 45 #include <sys/vnode.h> 46 #include <sys/sunddi.h> 47 48 #include <netsmb/smb_osdep.h> 49 50 #include <netsmb/smb.h> 51 #include <netsmb/smb2.h> 52 #include <netsmb/smb_conn.h> 53 #include <netsmb/smb_subr.h> 54 #include <netsmb/smb_rq.h> 55 56 #include <smbfs/smbfs.h> 57 #include <smbfs/smbfs_node.h> 58 #include <smbfs/smbfs_subr.h> 59 60 /* 61 * In the Darwin code, this function used to compute the full path 62 * by following the chain of n_parent pointers back to the root. 63 * In the Solaris port we found the n_parent pointers inconvenient 64 * because they hold parent nodes busy. We now keep the full path 65 * in every node, so this function need only marshall the directory 66 * path, and (if provided) the separator and last component name. 67 * 68 * Note that this logic must match that in smbfs_getino 69 */ 70 int 71 smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, 72 const char *name, int nmlen, u_int8_t sep) 73 { 74 int caseopt = SMB_CS_NONE; 75 int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0; 76 int error; 77 78 /* 79 * SMB1 may need an alignment pad before (not SMB2) 80 */ 81 if (((vcp)->vc_flags & SMBV_SMB2) == 0 && 82 ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) { 83 error = mb_put_padbyte(mbp); 84 if (error) 85 return (error); 86 } 87 88 error = smb_put_dmem(mbp, vcp, 89 dnp->n_rpath, dnp->n_rplen, 90 caseopt, NULL); 91 if (name) { 92 /* 93 * Special case at share root: 94 * Don't put another slash. 95 */ 96 if (dnp->n_rplen <= 1 && sep == '\\') 97 sep = 0; 98 /* 99 * More special cases, now for XATTR: 100 * Our "faked up" XATTR directories use a 101 * full path name ending with ":" so as to 102 * avoid conflicts with any real paths. 103 * (It is not a valid CIFS path name.) 104 * Therefore, when we're composing a full 105 * path name from an XATTR directory, we 106 * need to _ommit_ the ":" separator and 107 * instead copy the one from the "fake" 108 * parent node's path name. 109 */ 110 if (dnp->n_flag & N_XATTR) 111 sep = 0; 112 113 if (sep) { 114 /* Put the separator */ 115 if (unicode) 116 error = mb_put_uint16le(mbp, sep); 117 else 118 error = mb_put_uint8(mbp, sep); 119 if (error) 120 return (error); 121 } 122 /* Put the name */ 123 error = smb_put_dmem(mbp, vcp, 124 name, nmlen, caseopt, NULL); 125 if (error) 126 return (error); 127 } 128 129 /* SMB1 wants NULL termination. */ 130 if (((vcp)->vc_flags & SMBV_SMB2) == 0) { 131 if (unicode) 132 error = mb_put_uint16le(mbp, 0); 133 else 134 error = mb_put_uint8(mbp, 0); 135 } 136 137 return (error); 138 } 139 140 /* 141 * Convert a Unicode directory entry to UTF-8 142 */ 143 void 144 smbfs_fname_tolocal(struct smbfs_fctx *ctx) 145 { 146 uchar_t tmpbuf[SMB_MAXFNAMELEN+1]; 147 struct smb_vc *vcp = SSTOVC(ctx->f_ssp); 148 uchar_t *dst; 149 const ushort_t *src; 150 size_t inlen, outlen; 151 int flags; 152 153 if (ctx->f_nmlen == 0) 154 return; 155 156 if (!SMB_UNICODE_STRINGS(vcp)) 157 return; 158 159 if (ctx->f_namesz < sizeof (tmpbuf)) { 160 ASSERT(0); 161 goto errout; 162 } 163 164 /* 165 * In-place conversions are not supported, 166 * so convert into tmpbuf and copy. 167 */ 168 dst = tmpbuf; 169 outlen = SMB_MAXFNAMELEN; 170 /*LINTED*/ 171 src = (const ushort_t *)ctx->f_name; 172 inlen = ctx->f_nmlen / 2; /* number of UCS-2 characters */ 173 flags = UCONV_IN_LITTLE_ENDIAN; 174 175 if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) != 0) 176 goto errout; 177 178 ASSERT(outlen < sizeof (tmpbuf)); 179 tmpbuf[outlen] = '\0'; 180 bcopy(tmpbuf, ctx->f_name, outlen + 1); 181 ctx->f_nmlen = (int)outlen; 182 return; 183 184 errout: 185 /* 186 * Conversion failed, but our caller does not 187 * deal with errors here, so just put a "?". 188 * Don't expect to ever see this. 189 */ 190 (void) strlcpy(ctx->f_name, "?", ctx->f_namesz); 191 } 192 193 /* 194 * Decode a directory entry from OtW form into ctx->f_attr 195 * 196 * Caller already put some (wire-format) directory entries 197 * into ctx->f_mdchain and we expect to find one. 198 * 199 * Advancing correctly through the buffer can be tricky if one 200 * tries to add up the size of an entry as you go (which is how 201 * the darwin code this is derived from did it). The easiest way 202 * to correctly advance the position is to get a whole dirent 203 * into another mdchain (entry_mdc) based on NextEntryOffset, 204 * and then scan the data from that mdchain. On the last entry, 205 * we don't know the entire length, so just scan directly from 206 * what remains of the multi-entry buffer instead of trying to 207 * figure out the length to copy into a separate mdchain. 208 */ 209 int 210 smbfs_decode_dirent(struct smbfs_fctx *ctx) 211 { 212 struct mdchain entry_mdc; 213 struct mdchain *mdp = &ctx->f_mdchain; 214 size_t nmlen; 215 uint64_t llongint; 216 uint32_t nmsize, dattr; 217 uint32_t nextoff = 0; 218 int error; 219 220 /* In case we error out... */ 221 ctx->f_nmlen = 0; 222 ctx->f_rkey = (uint32_t)-1; 223 bzero(&entry_mdc, sizeof (entry_mdc)); 224 225 /* 226 * Setup mdp to point to an mbchain holding 227 * what should be a single directory entry. 228 */ 229 error = md_get_uint32le(mdp, &nextoff); 230 if (error != 0) 231 goto errout; 232 if (nextoff >= 4) { 233 /* 234 * More entries follow. Make a new mbchain 235 * holding just this one entry, then advance. 236 */ 237 mblk_t *m = NULL; 238 error = md_get_mbuf(mdp, nextoff - 4, &m); 239 if (error != 0) 240 goto errout; 241 md_initm(&entry_mdc, m); 242 mdp = &entry_mdc; 243 ctx->f_eofs += nextoff; 244 } else { 245 /* Scan directly from ctx->f_mdchain */ 246 ctx->f_eofs = ctx->f_left; 247 } 248 249 /* 250 * Decode the fixed-size parts 251 */ 252 switch (ctx->f_infolevel) { 253 case FileFullDirectoryInformation: 254 case SMB_FIND_FULL_DIRECTORY_INFO: 255 md_get_uint32le(mdp, &ctx->f_rkey); /* resume key (idx) */ 256 md_get_uint64le(mdp, &llongint); /* creation time */ 257 smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime); 258 md_get_uint64le(mdp, &llongint); 259 smb_time_NT2local(llongint, &ctx->f_attr.fa_atime); 260 md_get_uint64le(mdp, &llongint); 261 smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime); 262 md_get_uint64le(mdp, &llongint); 263 smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime); 264 md_get_uint64le(mdp, &llongint); /* file size */ 265 ctx->f_attr.fa_size = llongint; 266 md_get_uint64le(mdp, &llongint); /* alloc. size */ 267 ctx->f_attr.fa_allocsz = llongint; 268 md_get_uint32le(mdp, &dattr); /* ext. file attributes */ 269 ctx->f_attr.fa_attr = dattr; 270 error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */ 271 if (error) 272 goto errout; 273 md_get_uint32le(mdp, NULL); /* Ea size */ 274 break; 275 276 case FileStreamInformation: 277 error = md_get_uint32le(mdp, &nmsize); /* name size (otw) */ 278 md_get_uint64le(mdp, &llongint); /* file size */ 279 ctx->f_attr.fa_size = llongint; 280 md_get_uint64le(mdp, &llongint); /* alloc. size */ 281 ctx->f_attr.fa_allocsz = llongint; 282 /* 283 * Stream names start with a ':' that we want to skip. 284 * This is the easiest place to take care of that. 285 * Always unicode here. 286 */ 287 if (nmsize >= 2) { 288 struct mdchain save_mdc; 289 uint16_t wch; 290 save_mdc = *mdp; 291 md_get_uint16le(mdp, &wch); 292 if (wch == ':') { 293 /* OK, we skipped the ':' */ 294 nmsize -= 2; 295 } else { 296 SMBVDEBUG("No leading : in stream?\n"); 297 /* restore position */ 298 *mdp = save_mdc; 299 } 300 } 301 break; 302 303 default: 304 SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel); 305 error = EINVAL; 306 goto errout; 307 } 308 309 /* 310 * Get the filename, and convert to utf-8 311 * Allocated f_name in findopen 312 */ 313 nmlen = ctx->f_namesz; 314 error = smb_get_dstring(mdp, SSTOVC(ctx->f_ssp), 315 ctx->f_name, &nmlen, nmsize); 316 if (error != 0) 317 goto errout; 318 ctx->f_nmlen = (int)nmlen; 319 md_done(&entry_mdc); 320 return (0); 321 322 errout: 323 /* 324 * Something bad has happened and we ran out of data 325 * before we could parse all f_ecnt entries expected. 326 * Give up on the current buffer. 327 */ 328 SMBVDEBUG("ran out of data\n"); 329 ctx->f_eofs = ctx->f_left; 330 md_done(&entry_mdc); 331 return (error); 332 } 333 334 /* 335 * Decode FileAllInformation 336 * 337 * The data is a concatenation of: 338 * FileBasicInformation 339 * FileStandardInformation 340 * FileInternalInformation 341 * FileEaInformation 342 * FileAccessInformation 343 * FilePositionInformation 344 * FileModeInformation 345 * FileAlignmentInformation 346 * FileNameInformation 347 */ 348 /*ARGSUSED*/ 349 int 350 smbfs_decode_file_all_info(struct smb_share *ssp, 351 struct mdchain *mdp, struct smbfattr *fap) 352 { 353 uint64_t llongint, lsize; 354 uint32_t dattr; 355 int error; 356 357 /* 358 * This part is: FileBasicInformation 359 */ 360 361 /* creation time */ 362 md_get_uint64le(mdp, &llongint); 363 smb_time_NT2local(llongint, &fap->fa_createtime); 364 365 /* last access time */ 366 md_get_uint64le(mdp, &llongint); 367 smb_time_NT2local(llongint, &fap->fa_atime); 368 369 /* last write time */ 370 md_get_uint64le(mdp, &llongint); 371 smb_time_NT2local(llongint, &fap->fa_mtime); 372 373 /* last change time */ 374 md_get_uint64le(mdp, &llongint); 375 smb_time_NT2local(llongint, &fap->fa_ctime); 376 377 /* attributes */ 378 md_get_uint32le(mdp, &dattr); 379 fap->fa_attr = dattr; 380 381 /* reserved */ 382 md_get_uint32le(mdp, NULL); 383 384 /* 385 * This part is: FileStandardInformation 386 */ 387 388 /* allocation size */ 389 md_get_uint64le(mdp, &lsize); 390 fap->fa_allocsz = lsize; 391 392 /* File size */ 393 error = md_get_uint64le(mdp, &lsize); 394 fap->fa_size = lsize; 395 396 /* 397 * There's more after this but we don't need it: 398 * Remainder of FileStandardInformation 399 * NumLlinks, DeletOnClose, IsDir, reserved. 400 * Then: 401 * FileInternalInformation 402 * FileEaInformation 403 * FileAccessInformation 404 * FilePositionInformation 405 * FileModeInformation 406 * FileAlignmentInformation 407 * FileNameInformation 408 */ 409 410 return (error); 411 } 412 413 /* 414 * Decode FileFsAttributeInformation 415 * 416 * ULONG FileSystemAttributes; 417 * LONG MaximumComponentNameLength; 418 * ULONG FileSystemNameLength; 419 * WCHAR FileSystemName[1]; 420 */ 421 int 422 smbfs_decode_fs_attr_info(struct smb_share *ssp, 423 struct mdchain *mdp, struct smb_fs_attr_info *fsa) 424 { 425 struct smb_vc *vcp = SSTOVC(ssp); 426 uint32_t nlen; 427 int error; 428 429 md_get_uint32le(mdp, &fsa->fsa_aflags); 430 md_get_uint32le(mdp, &fsa->fsa_maxname); 431 error = md_get_uint32le(mdp, &nlen); /* fs name length */ 432 if (error) 433 goto out; 434 435 /* 436 * Get the FS type name. 437 */ 438 bzero(fsa->fsa_tname, FSTYPSZ); 439 if (SMB_UNICODE_STRINGS(vcp)) { 440 uint16_t tmpbuf[FSTYPSZ]; 441 size_t tmplen, outlen; 442 443 if (nlen > sizeof (tmpbuf)) 444 nlen = sizeof (tmpbuf); 445 error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM); 446 if (error != 0) 447 goto out; 448 tmplen = nlen / 2; /* UCS-2 chars */ 449 outlen = FSTYPSZ - 1; 450 error = uconv_u16tou8(tmpbuf, &tmplen, 451 (uchar_t *)fsa->fsa_tname, &outlen, 452 UCONV_IN_LITTLE_ENDIAN); 453 } else { 454 if (nlen > (FSTYPSZ - 1)) 455 nlen = FSTYPSZ - 1; 456 error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM); 457 } 458 459 out: 460 return (error); 461 } 462