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 vnode_t *vp; 228 229 *vpp = NULL; 230 231 /* Don't expect "." or ".." here anymore. */ 232 if ((nmlen == 1 && name[0] == '.') || 233 (nmlen == 2 && name[0] == '.' && name[1] == '.')) { 234 DEBUG_ENTER("smbfs_nget: name is '.' or '..'"); 235 return (EINVAL); 236 } 237 238 /* The real work is in this call... */ 239 vp = smbfs_make_node(dvp->v_vfsp, 240 dnp->n_rpath, dnp->n_rplen, 241 name, nmlen, fap); 242 243 /* 244 * We always have a vp now, because 245 * smbfs_make_node / make_smbnode 246 * calls kmem_alloc with KM_SLEEP. 247 */ 248 ASSERT(vp); 249 250 #ifdef NOT_YET 251 /* update the attr_cache info if the file is clean */ 252 if (fap && !(VTOSMB(vp)->n_flag & NFLUSHWIRE)) 253 smbfs_attr_cacheenter(vp, fap); 254 if (dvp && makeentry) { 255 /* add entry to DNLC */ 256 cache_enter(dvp, vp, &cn); 257 } 258 #endif /* NOT_YET */ 259 260 /* BSD symlink hack removed (smb_symmagic) */ 261 262 #ifdef NOT_YET 263 smbfs_attr_cacheenter(vp, fap); /* update the attr_cache info */ 264 #endif /* NOT_YET */ 265 266 *vpp = vp; 267 268 return (0); 269 } 270 271 /* 272 * routines to maintain vnode attributes cache 273 * smbfs_attr_cacheenter: unpack np.i to vnode_vattr structure 274 * 275 * Note that some SMB servers do not exhibit POSIX behaviour 276 * with regard to the mtime on directories. To work around 277 * this, we never allow the mtime on a directory to go backwards, 278 * and bump it forwards elsewhere to simulate the correct 279 * behaviour. 280 */ 281 void 282 smbfs_attr_cacheenter(vnode_t *vp, struct smbfattr *fap) 283 { 284 struct smbnode *np = VTOSMB(vp); 285 int vtype; 286 struct timespec ts; 287 288 mutex_enter(&np->r_statelock); 289 290 vtype = vp->v_type; 291 if (vtype == VREG) { 292 if (np->n_size != fap->fa_size) { 293 /* 294 * Had Darwin ubc_sync_range call here, 295 * invalidating the truncated range. 296 * XXX: Solaris equivalent? 297 */ 298 SMBVDEBUG("Update size?\n"); 299 } 300 np->n_size = fap->fa_size; 301 } else if (vtype == VDIR) { 302 np->n_size = 16384; /* XXX should be a better way ... */ 303 /* 304 * Don't allow mtime to go backwards. 305 * Yes this has its flaws. Better ideas are welcome! 306 */ 307 /*CSTYLED*/ 308 if (timespeccmp(&fap->fa_mtime, &np->n_mtime, <)) 309 fap->fa_mtime = np->n_mtime; 310 } else if (vtype != VLNK) 311 goto out; 312 313 np->n_atime = fap->fa_atime; 314 np->n_ctime = fap->fa_ctime; 315 np->n_mtime = fap->fa_mtime; 316 np->n_dosattr = fap->fa_attr; 317 318 np->n_flag &= ~NATTRCHANGED; 319 gethrestime(&ts); 320 np->n_attrage = ts.tv_sec; 321 322 out: 323 mutex_exit(&np->r_statelock); 324 } 325 326 int 327 smbfs_attr_cachelookup(vnode_t *vp, struct vattr *vap) 328 { 329 struct smbnode *np = VTOSMB(vp); 330 struct smbmntinfo *smi = VTOSMI(vp); 331 time_t attrtimeo; 332 struct timespec ts, *stime; 333 mode_t type; 334 335 /* 336 * Determine attrtimeo. It will be something between SMB_MINATTRTIMO and 337 * SMB_MAXATTRTIMO where recently modified files have a short timeout 338 * and files that haven't been modified in a long time have a long 339 * timeout. This is the same algorithm used by NFS. 340 */ 341 gethrestime(&ts); 342 stime = &np->r_mtime; 343 attrtimeo = (ts.tv_sec - stime->tv_sec) / 10; 344 if (attrtimeo < SMB_MINATTRTIMO) { 345 attrtimeo = SMB_MINATTRTIMO; 346 } else if (attrtimeo > SMB_MAXATTRTIMO) 347 attrtimeo = SMB_MAXATTRTIMO; 348 /* has too much time passed? */ 349 stime = (struct timespec *)&np->r_attrtime; 350 if ((ts.tv_sec - stime->tv_sec) > attrtimeo) 351 return (ENOENT); 352 353 if (!vap) 354 return (0); 355 356 switch (vp->v_type) { 357 case VREG: 358 type = S_IFREG; 359 break; 360 case VLNK: 361 type = S_IFLNK; 362 break; 363 case VDIR: 364 type = S_IFDIR; 365 break; 366 default: 367 SMBSDEBUG("unknown vnode_vtype %d\n", vp->v_type); 368 return (EINVAL); 369 } 370 371 mutex_enter(&np->r_statelock); 372 373 if (!(np->n_flag & NGOTIDS)) { 374 np->n_mode = type; 375 #ifdef APPLE 376 if (smi->smi_fsattr & FILE_PERSISTENT_ACLS) { 377 /* XXX: Can this block? Drop r_statelock? */ 378 if (!smbfs_getids(np, scredp)) { 379 np->n_flag |= NGOTIDS; 380 np->n_mode |= ACCESSPERMS; /* 0777 */ 381 } 382 } 383 #endif /* APPLE */ 384 if (!(np->n_flag & NGOTIDS)) { 385 np->n_flag |= NGOTIDS; 386 np->n_uid = smi->smi_args.uid; 387 np->n_gid = smi->smi_args.gid; 388 } 389 } 390 391 if (vap->va_mask & AT_TYPE) 392 vap->va_type = vp->v_type; 393 if (vap->va_mask & AT_MODE) { 394 np->n_mode = 0; 395 if (vp->v_type == VDIR) 396 np->n_mode |= smi->smi_args.dir_mode; 397 else /* symlink and regular file */ 398 np->n_mode |= smi->smi_args.file_mode; 399 vap->va_mode = np->n_mode; 400 } 401 if (vap->va_mask & AT_SIZE) 402 vap->va_size = np->n_size; 403 if (vap->va_mask & AT_NODEID) 404 vap->va_nodeid = np->n_ino; 405 if (vap->va_mask & AT_ATIME) 406 vap->va_atime = np->n_atime; 407 if (vap->va_mask & AT_CTIME) 408 vap->va_ctime = np->n_ctime; 409 if (vap->va_mask & AT_MTIME) 410 vap->va_mtime = np->n_mtime; 411 vap->va_nlink = 1; 412 vap->va_uid = np->n_uid; 413 vap->va_gid = np->n_gid; 414 vap->va_fsid = vp->v_vfsp->vfs_dev; 415 vap->va_rdev = 0; 416 vap->va_blksize = MAXBSIZE; 417 vap->va_nblocks = (fsblkcnt64_t)btod(np->n_size); 418 vap->va_seq = 0; 419 420 mutex_exit(&np->r_statelock); 421 422 return (0); 423 } 424 425 /* 426 * Some SMB servers don't exhibit POSIX behaviour with regard to 427 * updating the directory mtime when the directory's contents 428 * change. 429 * 430 * We force the issue here by updating our cached copy of the mtime 431 * whenever we perform such an action ourselves, and then mark the 432 * cache invalid. Subsequently when the invalidated cache entry is 433 * updated, we disallow an update that would move the mtime backwards. 434 * 435 * This preserves correct or near-correct behaviour with a 436 * compliant server, and gives near-correct behaviour with 437 * a non-compliant server in the most common case (we are the 438 * only client changing the directory). 439 * 440 * There are also complications if a server's time is ahead 441 * of our own. We must 'touch' a directory when it is first 442 * created, to ensure that the timestamp starts out sane, 443 * however it may have a timestamp well ahead of the 'touch' 444 * point which will be returned and cached the first time the 445 * directory's attributes are fetched. Subsequently, the 446 * directory's mtime will not appear to us to change at all 447 * until our local time catches up to the server. 448 * 449 * Thus, any time a directory is 'touched', the saved timestamp 450 * must advance at least far enough forwards to be visible to 451 * the stat(2) interface. 452 * 453 * XXX note that better behaviour with non-compliant servers 454 * could be obtained by bumping the mtime forwards when 455 * an update for an invalidated entry returns a nonsensical 456 * mtime. 457 */ 458 459 void 460 smbfs_attr_touchdir(struct smbnode *dnp) 461 { 462 struct timespec ts, ta; 463 464 mutex_enter(&dnp->r_statelock); 465 466 /* 467 * XXX - not sure about this... 468 * Creep the saved time forwards far enough that 469 * layers above the kernel will notice. 470 */ 471 ta.tv_sec = 1; 472 ta.tv_nsec = 0; 473 timespecadd(&dnp->n_mtime, &ta); 474 /* 475 * If the current time is later than the updated 476 * saved time, apply it instead. 477 */ 478 gethrestime(&ts); 479 /*CSTYLED*/ 480 if (timespeccmp(&dnp->n_mtime, &ts, <)) 481 dnp->n_mtime = ts; 482 /* 483 * Invalidate the cache, so that we go to the wire 484 * to check that the server doesn't have a better 485 * timestamp next time we care. 486 */ 487 smbfs_attr_cacheremove(dnp); 488 mutex_exit(&dnp->r_statelock); 489 } 490