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