38 #include <sys/stat.h>
39 #include <sys/debug.h>
40 #include <sys/policy.h>
41 #include <sys/fs/tmpnode.h>
42 #include <sys/fs/tmp.h>
43 #include <sys/vtrace.h>
44
45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
47 char *, struct tmpnode *, struct tdirent *, struct cred *);
48 static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
49 static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
50 enum de_op, struct tmpnode **, struct cred *);
51 static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
52 enum de_op, struct tmpnode *);
53
54
55 #define T_HASH_SIZE 8192 /* must be power of 2 */
56 #define T_MUTEX_SIZE 64
57
58 static struct tdirent *t_hashtable[T_HASH_SIZE];
59 static kmutex_t t_hashmutex[T_MUTEX_SIZE];
60
61 #define T_HASH_INDEX(a) ((a) & (T_HASH_SIZE-1))
62 #define T_MUTEX_INDEX(a) ((a) & (T_MUTEX_SIZE-1))
63
64 #define TMPFS_HASH(tp, name, hash) \
65 { \
66 char Xc, *Xcp; \
67 hash = (uint_t)(uintptr_t)(tp) >> 8; \
68 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
69 hash = (hash << 4) + hash + (uint_t)Xc; \
70 }
71
72 void
73 tmpfs_hash_init(void)
74 {
75 int ix;
76
77 for (ix = 0; ix < T_MUTEX_SIZE; ix++)
250 */
251 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
252 ASSERT(dir->tn_type == VDIR);
253
254 /*
255 * Don't allow '/' characters in pathname component
256 * (thus in ufs_direnter()).
257 */
258 for (s = name; *s; s++)
259 if (*s == '/')
260 return (EACCES);
261
262 if (name[0] == '\0')
263 panic("tdirenter: NULL name");
264
265 /*
266 * For link and rename lock the source entry and check the link count
267 * to see if it has been removed while it was unlocked.
268 */
269 if (op == DE_LINK || op == DE_RENAME) {
270 if (tp != dir)
271 rw_enter(&tp->tn_rwlock, RW_WRITER);
272 mutex_enter(&tp->tn_tlock);
273 if (tp->tn_nlink == 0) {
274 mutex_exit(&tp->tn_tlock);
275 if (tp != dir)
276 rw_exit(&tp->tn_rwlock);
277 return (ENOENT);
278 }
279
280 if (tp->tn_nlink == MAXLINK) {
281 mutex_exit(&tp->tn_tlock);
282 if (tp != dir)
283 rw_exit(&tp->tn_rwlock);
284 return (EMLINK);
285 }
286 tp->tn_nlink++;
287 gethrestime(&tp->tn_ctime);
288 mutex_exit(&tp->tn_tlock);
289 if (tp != dir)
290 rw_exit(&tp->tn_rwlock);
291 }
|
38 #include <sys/stat.h>
39 #include <sys/debug.h>
40 #include <sys/policy.h>
41 #include <sys/fs/tmpnode.h>
42 #include <sys/fs/tmp.h>
43 #include <sys/vtrace.h>
44
45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
47 char *, struct tmpnode *, struct tdirent *, struct cred *);
48 static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
49 static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
50 enum de_op, struct tmpnode **, struct cred *);
51 static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
52 enum de_op, struct tmpnode *);
53
54
55 #define T_HASH_SIZE 8192 /* must be power of 2 */
56 #define T_MUTEX_SIZE 64
57
58 /* Non-static so compilers won't constant-fold these away. */
59 clock_t tmpfs_rename_backoff_delay = 1;
60 unsigned int tmpfs_rename_backoff_tries = 0;
61 unsigned long tmpfs_rename_loops = 0;
62
63 static struct tdirent *t_hashtable[T_HASH_SIZE];
64 static kmutex_t t_hashmutex[T_MUTEX_SIZE];
65
66 #define T_HASH_INDEX(a) ((a) & (T_HASH_SIZE-1))
67 #define T_MUTEX_INDEX(a) ((a) & (T_MUTEX_SIZE-1))
68
69 #define TMPFS_HASH(tp, name, hash) \
70 { \
71 char Xc, *Xcp; \
72 hash = (uint_t)(uintptr_t)(tp) >> 8; \
73 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
74 hash = (hash << 4) + hash + (uint_t)Xc; \
75 }
76
77 void
78 tmpfs_hash_init(void)
79 {
80 int ix;
81
82 for (ix = 0; ix < T_MUTEX_SIZE; ix++)
255 */
256 ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
257 ASSERT(dir->tn_type == VDIR);
258
259 /*
260 * Don't allow '/' characters in pathname component
261 * (thus in ufs_direnter()).
262 */
263 for (s = name; *s; s++)
264 if (*s == '/')
265 return (EACCES);
266
267 if (name[0] == '\0')
268 panic("tdirenter: NULL name");
269
270 /*
271 * For link and rename lock the source entry and check the link count
272 * to see if it has been removed while it was unlocked.
273 */
274 if (op == DE_LINK || op == DE_RENAME) {
275 if (tp != dir) {
276 unsigned int tries = 0;
277
278 /*
279 * If we are acquiring tp->tn_rwlock (for SOURCE)
280 * inside here, we must consider the following:
281 *
282 * - dir->tn_rwlock (TARGET) is already HELD (see
283 * above ASSERT()).
284 *
285 * - It is possible our SOURCE is a parent of our
286 * TARGET. Yes it's unusual, but it will return an
287 * error below via tdircheckpath().
288 *
289 * - It is also possible that another thread,
290 * concurrent to this one, is performing
291 * rmdir(TARGET), which means it will first acquire
292 * SOURCE's lock, THEN acquire TARGET's lock, which
293 * could result in this thread holding TARGET and
294 * trying for SOURCE, but the other thread holding
295 * SOURCE and trying for TARGET. This is deadlock,
296 * and it's inducible.
297 *
298 * To prevent this, we borrow some techniques from UFS
299 * and rw_tryenter(), delaying if we fail, and
300 * if someone tweaks the number of backoff tries to be
301 * nonzero, return EBUSY after that number of tries.
302 */
303 while (!rw_tryenter(&tp->tn_rwlock, RW_WRITER)) {
304 /*
305 * Sloppy, but this is a diagnostic so atomic
306 * increment would be overkill.
307 */
308 tmpfs_rename_loops++;
309
310 if (tmpfs_rename_backoff_tries != 0) {
311 if (tries > tmpfs_rename_backoff_tries)
312 return (EBUSY);
313 tries++;
314 }
315 /*
316 * NOTE: We're still holding dir->tn_rwlock,
317 * so drop it over the delay, so any other
318 * thread can get its business done.
319 *
320 * No state change or state inspection happens
321 * prior to here, so it is not wholly dangerous
322 * to release-and-reacquire dir->tn_rwlock.
323 *
324 * Hold the vnode of dir in case it gets
325 * released by another thread, though.
326 */
327 VN_HOLD(TNTOV(dir));
328 rw_exit(&dir->tn_rwlock);
329 delay(tmpfs_rename_backoff_delay);
330 rw_enter(&dir->tn_rwlock, RW_WRITER);
331 VN_RELE(TNTOV(dir));
332 }
333 }
334 mutex_enter(&tp->tn_tlock);
335 if (tp->tn_nlink == 0) {
336 mutex_exit(&tp->tn_tlock);
337 if (tp != dir)
338 rw_exit(&tp->tn_rwlock);
339 return (ENOENT);
340 }
341
342 if (tp->tn_nlink == MAXLINK) {
343 mutex_exit(&tp->tn_tlock);
344 if (tp != dir)
345 rw_exit(&tp->tn_rwlock);
346 return (EMLINK);
347 }
348 tp->tn_nlink++;
349 gethrestime(&tp->tn_ctime);
350 mutex_exit(&tp->tn_tlock);
351 if (tp != dir)
352 rw_exit(&tp->tn_rwlock);
353 }
|