xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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