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_node.c,v 1.54.52.1 2005/05/27 02:35:28 lindak Exp $ 33 */ 34 35 /* 36 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37 * Use is subject to license terms. 38 */ 39 40 #pragma ident "%Z%%M% %I% %E% SMI" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/cred.h> 45 #include <sys/vfs.h> 46 #include <sys/vnode.h> 47 #include <sys/kmem.h> 48 #include <sys/stat.h> 49 #include <sys/atomic.h> 50 #include <sys/cmn_err.h> 51 #include <sys/sysmacros.h> 52 #include <sys/bitmap.h> 53 54 #ifdef APPLE 55 #include <sys/smb_apple.h> 56 #else 57 #include <netsmb/smb_osdep.h> 58 #endif 59 60 #include <netsmb/smb.h> 61 #include <netsmb/smb_conn.h> 62 #include <netsmb/smb_subr.h> 63 64 #include <smbfs/smbfs.h> 65 #include <smbfs/smbfs_node.h> 66 #include <smbfs/smbfs_subr.h> 67 68 #if defined(DEBUG) || defined(lint) 69 #define SMBFS_NAME_DEBUG 70 #endif 71 72 /* 73 * Lack of inode numbers leads us to the problem of generating them. 74 * Partially this problem can be solved by having a dir/file cache 75 * with inode numbers generated from the incremented by one counter. 76 * However this way will require too much kernel memory, gives all 77 * sorts of locking and consistency problems, not to mentinon counter 78 * overflows. So, I'm decided to use a hash function to generate 79 * pseudo random (and [often?] unique) inode numbers. 80 */ 81 82 /* Magic constants for name hashing. */ 83 #define FNV_32_PRIME ((uint32_t)0x01000193UL) 84 #define FNV1_32_INIT ((uint32_t)33554467UL) 85 86 uint32_t 87 smbfs_hash3(uint32_t ival, const char *name, int nmlen) 88 { 89 uint32_t v; 90 91 for (v = ival; nmlen; name++, nmlen--) { 92 v *= FNV_32_PRIME; 93 v ^= (uint32_t)*name; 94 } 95 return (v); 96 } 97 98 uint32_t 99 smbfs_hash(const char *name, int nmlen) 100 { 101 uint32_t v; 102 103 v = smbfs_hash3(FNV1_32_INIT, name, nmlen); 104 return (v); 105 } 106 107 /* 108 * This is basically a hash of the full path name, but 109 * computed without having the full path contiguously. 110 * The path building logic needs to match what 111 * smbfs_fullpath does. 112 * 113 * Note that smbfs_make_node computes inode numbers by 114 * calling smbfs_hash on the full path name. This will 115 * compute the same result given the directory path and 116 * the last component separately. 117 */ 118 uint32_t 119 smbfs_getino(struct smbnode *dnp, const char *name, int nmlen) 120 { 121 uint32_t ino; 122 123 /* Start with directory hash */ 124 ino = (uint32_t)dnp->n_ino; 125 126 /* 127 * If not the root, hash a slash. 128 */ 129 if (dnp->n_rplen > 1) 130 ino = smbfs_hash3(ino, "\\", 1); 131 132 /* Now hash this component. */ 133 ino = smbfs_hash3(ino, name, nmlen); 134 135 return (ino); 136 } 137 138 #define CHAR_FC '\374' /* 0xFC */ 139 #define CHAR_FE '\376' /* 0xFE */ 140 char * 141 smbfs_name_alloc(const char *name, int nmlen) 142 { 143 char *cp; 144 size_t alen; 145 146 #ifdef SMBFS_NAME_DEBUG 147 /* 148 * Note: The passed length is strlen(name), 149 * and does NOT include the terminating nul. 150 * Allocated space holds: (in order) 151 * (int)strlen 152 * char 0xFC (1st marker) 153 * copy of string 154 * terminating null 155 * char 0xFE (2nd marker) 156 */ 157 alen = sizeof (int) + 1 + nmlen + 1 + 1; 158 cp = kmem_alloc(alen, KM_SLEEP); 159 /*LINTED*/ 160 *(int *)cp = nmlen; 161 cp += sizeof (int); 162 cp[0] = CHAR_FC; 163 cp++; 164 bcopy(name, cp, nmlen); 165 cp[nmlen] = 0; 166 cp[nmlen + 1] = CHAR_FE; 167 #else 168 alen = nmlen + 1; /* Passed length does NOT include the nul. */ 169 cp = kmem_alloc(alen, KM_SLEEP); 170 bcopy(name, cp, nmlen); 171 cp[nmlen] = 0; 172 #endif 173 return (cp); 174 } 175 176 /* 177 * Note: Passed length does NOT include the nul, 178 * the same as with smbfs_name_alloc(). 179 */ 180 void 181 smbfs_name_free(const char *name, int nmlen) 182 { 183 size_t alen; 184 #ifdef SMBFS_NAME_DEBUG 185 int lnmlen; 186 char *cp; 187 188 /* 189 * See comment in smbfs_name_alloc 190 * about the layout of this memory. 191 */ 192 alen = sizeof (int) + 1 + nmlen + 1 + 1; 193 cp = (char *)name; 194 cp--; 195 if (*cp != CHAR_FC) { 196 debug_enter("smbfs_name_free: name[-1] != 0xFC"); 197 } 198 cp -= sizeof (int); 199 /*LINTED*/ 200 lnmlen = *(int *)cp; 201 if (lnmlen != nmlen) { 202 debug_enter("smbfs_name_free: name[-5] != nmlen"); 203 } 204 if (name[nmlen + 1] != CHAR_FE) { 205 debug_enter("smbfs_name_free: name[nmlen+1] != 0xFE"); 206 } 207 kmem_free(cp, alen); 208 #else 209 alen = nmlen + 1; 210 kmem_free((char *)name, alen); 211 #endif 212 } 213 214 /* 215 * smbfs_nget() 216 * 217 * NOTES: 218 * 219 * It would be nice to be able to pass in a flag when the caller is sure 220 * that the node does not exist and should just be allocated. 221 */ 222 int 223 smbfs_nget(vnode_t *dvp, const char *name, int nmlen, 224 struct smbfattr *fap, vnode_t **vpp) 225 { 226 struct smbnode *dnp = VTOSMB(dvp); 227 struct smbnode *np; 228 vnode_t *vp; 229 char sep; 230 231 *vpp = NULL; 232 233 /* Don't expect "." or ".." here anymore. */ 234 if ((nmlen == 1 && name[0] == '.') || 235 (nmlen == 2 && name[0] == '.' && name[1] == '.')) { 236 DEBUG_ENTER("smbfs_nget: name is '.' or '..'"); 237 return (EINVAL); 238 } 239 240 /* 241 * See the comment near the top of smbfs_xattr.c about 242 * the logic for what separators to use where. 243 */ 244 sep = (dnp->n_flag & N_XATTR) ? 0 : '\\'; 245 246 /* Find or create the node. */ 247 vp = smbfs_make_node(dvp->v_vfsp, 248 dnp->n_rpath, dnp->n_rplen, 249 name, nmlen, sep, fap); 250 251 /* 252 * We always have a vp now, because 253 * smbfs_make_node / make_smbnode 254 * calls kmem_alloc with KM_SLEEP. 255 */ 256 ASSERT(vp); 257 np = VTOSMB(vp); 258 259 /* 260 * Files in an XATTR dir are also XATTR. 261 */ 262 if (dnp->n_flag & N_XATTR) { 263 mutex_enter(&np->r_statelock); 264 np->n_flag |= N_XATTR; 265 mutex_exit(&np->r_statelock); 266 } 267 268 #ifdef NOT_YET 269 /* update the attr_cache info if the file is clean */ 270 if (fap && !(VTOSMB(vp)->n_flag & NFLUSHWIRE)) 271 smbfs_attr_cacheenter(vp, fap); 272 if (dvp && makeentry) { 273 /* add entry to DNLC */ 274 cache_enter(dvp, vp, &cn); 275 } 276 #endif /* NOT_YET */ 277 278 /* BSD symlink hack removed (smb_symmagic) */ 279 280 #ifdef NOT_YET 281 smbfs_attr_cacheenter(vp, fap); /* update the attr_cache info */ 282 #endif /* NOT_YET */ 283 284 *vpp = vp; 285 286 return (0); 287 } 288 289 /* 290 * routines to maintain vnode attributes cache 291 * smbfs_attr_cacheenter: unpack np.i to vnode_vattr structure 292 * 293 * Note that some SMB servers do not exhibit POSIX behaviour 294 * with regard to the mtime on directories. To work around 295 * this, we never allow the mtime on a directory to go backwards, 296 * and bump it forwards elsewhere to simulate the correct 297 * behaviour. 298 */ 299 void 300 smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap) 301 { 302 struct smbnode *np = VTOSMB(vp); 303 int vtype; 304 struct timespec ts; 305 306 mutex_enter(&np->r_statelock); 307 308 vtype = (fap->fa_attr & SMB_FA_DIR) ? VDIR : VREG; 309 if (vp->v_type != vtype) 310 SMBVDEBUG("vtype change %d to %d\n", 311 vp->v_type, vtype); 312 vp->v_type = vtype; 313 314 if (vtype == VREG) { 315 if (np->n_size != fap->fa_size) { 316 /* 317 * Had Darwin ubc_sync_range call here, 318 * invalidating the truncated range. 319 * XXX: Solaris equivalent? 320 */ 321 SMBVDEBUG("Update size?\n"); 322 } 323 np->n_size = fap->fa_size; 324 } else if (vtype == VDIR) { 325 np->n_size = 16384; /* XXX should be a better way ... */ 326 /* 327 * Don't allow mtime to go backwards. 328 * Yes this has its flaws. Better ideas are welcome! 329 */ 330 /*CSTYLED*/ 331 if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <)) 332 fap->fa_mtime = np->n_mtime; 333 } else if (vtype != VLNK) 334 goto out; 335 336 np->n_atime = fap->fa_atime; 337 np->n_ctime = fap->fa_ctime; 338 np->n_mtime = fap->fa_mtime; 339 np->n_dosattr = fap->fa_attr; 340 341 np->n_flag &= ~NATTRCHANGED; 342 gethrestime(&ts); 343 np->n_attrage = ts.tv_sec; 344 345 out: 346 mutex_exit(&np->r_statelock); 347 } 348 349 int 350 smbfs_attr_cachelookup(vnode_t *vp, struct vattr *vap) 351 { 352 struct smbnode *np = VTOSMB(vp); 353 struct smbmntinfo *smi = VTOSMI(vp); 354 time_t attrtimeo; 355 struct timespec ts, *stime; 356 mode_t type; 357 358 /* 359 * Determine attrtimeo. It will be something between SMB_MINATTRTIMO and 360 * SMB_MAXATTRTIMO where recently modified files have a short timeout 361 * and files that haven't been modified in a long time have a long 362 * timeout. This is the same algorithm used by NFS. 363 */ 364 gethrestime(&ts); 365 stime = &np->r_mtime; 366 attrtimeo = (ts.tv_sec - stime->tv_sec) / 10; 367 if (attrtimeo < SMB_MINATTRTIMO) { 368 attrtimeo = SMB_MINATTRTIMO; 369 } else if (attrtimeo > SMB_MAXATTRTIMO) 370 attrtimeo = SMB_MAXATTRTIMO; 371 /* has too much time passed? */ 372 stime = (struct timespec *)&np->r_attrtime; 373 if ((ts.tv_sec - stime->tv_sec) > attrtimeo) 374 return (ENOENT); 375 376 if (!vap) 377 return (0); 378 379 switch (vp->v_type) { 380 case VREG: 381 type = S_IFREG; 382 break; 383 case VLNK: 384 type = S_IFLNK; 385 break; 386 case VDIR: 387 type = S_IFDIR; 388 break; 389 default: 390 SMBSDEBUG("unknown vnode_vtype %d\n", vp->v_type); 391 return (EINVAL); 392 } 393 394 mutex_enter(&np->r_statelock); 395 396 if (!(np->n_flag & NGOTIDS)) { 397 np->n_mode = type; 398 #ifdef APPLE 399 if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) { 400 /* XXX: Can this block? Drop r_statelock? */ 401 if (!smbfs_getids(np, scredp)) { 402 np->n_flag |= NGOTIDS; 403 np->n_mode |= ACCESSPERMS; /* 0777 */ 404 } 405 } 406 #endif /* APPLE */ 407 if (!(np->n_flag & NGOTIDS)) { 408 np->n_flag |= NGOTIDS; 409 np->n_uid = smi->smi_args.uid; 410 np->n_gid = smi->smi_args.gid; 411 } 412 } 413 414 if (vap->va_mask & AT_TYPE) 415 vap->va_type = vp->v_type; 416 if (vap->va_mask & AT_MODE) { 417 np->n_mode = 0; 418 if (vp->v_type == VDIR) 419 np->n_mode |= smi->smi_args.dir_mode; 420 else /* symlink and regular file */ 421 np->n_mode |= smi->smi_args.file_mode; 422 vap->va_mode = np->n_mode; 423 } 424 if (vap->va_mask & AT_SIZE) 425 vap->va_size = np->n_size; 426 if (vap->va_mask & AT_NODEID) 427 vap->va_nodeid = np->n_ino; 428 if (vap->va_mask & AT_ATIME) 429 vap->va_atime = np->n_atime; 430 if (vap->va_mask & AT_CTIME) 431 vap->va_ctime = np->n_ctime; 432 if (vap->va_mask & AT_MTIME) 433 vap->va_mtime = np->n_mtime; 434 vap->va_nlink = 1; 435 vap->va_uid = np->n_uid; 436 vap->va_gid = np->n_gid; 437 vap->va_fsid = vp->v_vfsp->vfs_dev; 438 vap->va_rdev = 0; 439 vap->va_blksize = MAXBSIZE; 440 vap->va_nblocks = (fsblkcnt64_t)btod(np->n_size); 441 vap->va_seq = 0; 442 443 mutex_exit(&np->r_statelock); 444 445 return (0); 446 } 447 448 /* 449 * Some SMB servers don't exhibit POSIX behaviour with regard to 450 * updating the directory mtime when the directory's contents 451 * change. 452 * 453 * We force the issue here by updating our cached copy of the mtime 454 * whenever we perform such an action ourselves, and then mark the 455 * cache invalid. Subsequently when the invalidated cache entry is 456 * updated, we disallow an update that would move the mtime backwards. 457 * 458 * This preserves correct or near-correct behaviour with a 459 * compliant server, and gives near-correct behaviour with 460 * a non-compliant server in the most common case (we are the 461 * only client changing the directory). 462 * 463 * There are also complications if a server's time is ahead 464 * of our own. We must 'touch' a directory when it is first 465 * created, to ensure that the timestamp starts out sane, 466 * however it may have a timestamp well ahead of the 'touch' 467 * point which will be returned and cached the first time the 468 * directory's attributes are fetched. Subsequently, the 469 * directory's mtime will not appear to us to change at all 470 * until our local time catches up to the server. 471 * 472 * Thus, any time a directory is 'touched', the saved timestamp 473 * must advance at least far enough forwards to be visible to 474 * the stat(2) interface. 475 * 476 * XXX note that better behaviour with non-compliant servers 477 * could be obtained by bumping the mtime forwards when 478 * an update for an invalidated entry returns a nonsensical 479 * mtime. 480 */ 481 482 void 483 smbfs_attr_touchdir(struct smbnode *dnp) 484 { 485 struct timespec ts, ta; 486 487 mutex_enter(&dnp->r_statelock); 488 489 /* 490 * XXX - not sure about this... 491 * Creep the saved time forwards far enough that 492 * layers above the kernel will notice. 493 */ 494 ta.tv_sec = 1; 495 ta.tv_nsec = 0; 496 timespecadd(&dnp->n_mtime, &ta); 497 /* 498 * If the current time is later than the updated 499 * saved time, apply it instead. 500 */ 501 gethrestime(&ts); 502 /*CSTYLED*/ 503 if (timespeccmp(&dnp->n_mtime, &ts, <)) 504 dnp->n_mtime = ts; 505 /* 506 * Invalidate the cache, so that we go to the wire 507 * to check that the server doesn't have a better 508 * timestamp next time we care. 509 */ 510 smbfs_attr_cacheremove(dnp); 511 mutex_exit(&dnp->r_statelock); 512 } 513