Bug Summary

File:obj-scan-build/ftpfs/../../ftpfs/dir.c
Location:line 199, column 32
Description:Use of memory after it is freed

Annotated Source Code

1/* Directory operations
2
3 Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
4 Written by Miles Bader <miles@gnu.org>
5 This file is part of the GNU Hurd.
6
7 The GNU Hurd is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2, or (at
10 your option) any later version.
11
12 The GNU Hurd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
20
21#include <unistd.h>
22#include <string.h>
23
24#include <hurd/netfs.h>
25
26#include "ftpfs.h"
27#include "ccache.h"
28
29/* Free the directory entry E and all resources it consumes. */
30void
31free_entry (struct ftpfs_dir_entry *e)
32{
33 assert (! e->self_p)((! e->self_p) ? (void) (0) : __assert_fail ("! e->self_p"
, "../../ftpfs/dir.c", 33, __PRETTY_FUNCTION__))
; /* We should only free deleted nodes. */
34 free (e->name);
35 if (e->symlink_target)
12
Taking false branch
36 free (e->symlink_target);
37 free (e);
13
Memory is released
38}
39
40/* Put the directory entry E into the hash table HTABLE, of length HTABLE_LEN. */
41static void
42insert (struct ftpfs_dir_entry *e,
43 struct ftpfs_dir_entry **htable, size_t htable_len)
44{
45 struct ftpfs_dir_entry **t = &htable[e->hv % htable_len];
46 if (*t)
47 (*t)->self_p = &e->next;
48 e->next = *t;
49 e->self_p = t;
50 *t = e;
51}
52
53/* Replace DIR's hashtable with a new one of length NEW_LEN, retaining all
54 existing entries. */
55static error_t
56rehash (struct ftpfs_dir *dir, size_t new_len)
57{
58 int i;
59 size_t old_len = dir->htable_len;
60 struct ftpfs_dir_entry **old_htable = dir->htable;
61 struct ftpfs_dir_entry **new_htable =
62 malloc (new_len * sizeof (struct ftpfs_dir_entry *));
63
64 if (! new_htable)
65 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
66
67 memset (new_htable, 0, new_len * sizeof(struct ftpfs_dir_entry *));
68
69 for (i = 0; i < old_len; i++)
70 while (old_htable[i])
71 {
72 struct ftpfs_dir_entry *e = old_htable[i];
73
74 /* Remove E from the old table (don't bother to fixup
75 e->next->self_p). */
76 old_htable[i] = e->next;
77
78 insert (e, new_htable, new_len);
79 }
80
81 free (old_htable);
82
83 dir->htable = new_htable;
84 dir->htable_len = new_len;
85
86 return 0;
87}
88
89/* Calculate NAME's hash value. */
90static size_t
91hash (const char *name)
92{
93 size_t hv = 0;
94 while (*name)
95 hv = ((hv << 5) + *name++) & 0xFFFFFF;
96 return hv;
97}
98
99/* Lookup NAME in DIR and return its entry. If there is no such entry, and
100 ADD is true, then a new entry is allocated and returned, otherwise 0 is
101 returned (if ADD is true then 0 can be returned if a memory allocation
102 error occurs). */
103struct ftpfs_dir_entry *
104lookup (struct ftpfs_dir *dir, const char *name, int add)
105{
106 size_t hv = hash (name);
107 struct ftpfs_dir_entry *h = dir->htable[hv % dir->htable_len], *e = h;
108
109 while (e && strcmp (name, e->name) != 0)
110 e = e->next;
111
112 if (!e && add)
113 {
114 if (dir->num_entries > dir->htable_len)
115 /* Grow the hash table. */
116 if (rehash (dir, (dir->htable_len + 1) * 2 - 1) != 0)
117 return 0;
118
119 e = malloc (sizeof *e);
120 if (e)
121 {
122 e->hv = hv;
123 e->name = strdup (name);
124 e->node = 0;
125 e->dir = dir;
126 e->stat_timestamp = 0;
127 memset (&e->stat, 0, sizeof e->stat);
128 e->symlink_target = 0;
129 e->noent = 0;
130 e->valid = 0;
131 e->name_timestamp = e->stat_timestamp = 0;
132 e->ordered_next = 0;
133 e->ordered_self_p = 0;
134 e->next = 0;
135 e->self_p = 0;
136 insert (e, dir->htable, dir->htable_len);
137 dir->num_entries++;
138 }
139 }
140
141 return e;
142}
143
144/* Remove E from its position in the ordered_next chain. */
145static void
146ordered_unlink (struct ftpfs_dir_entry *e)
147{
148 if (e->ordered_self_p)
149 *e->ordered_self_p = e->ordered_next;
150 if (e->ordered_next)
151 e->ordered_next->self_p = e->ordered_self_p;
152}
153
154/* Delete E from its directory, freeing any resources it holds. */
155static void
156delete (struct ftpfs_dir_entry *e, struct ftpfs_dir *dir)
157{
158 dir->num_entries--;
159
160 /* Take out of the hash chain. */
161 if (e->self_p)
8
Taking false branch
162 *e->self_p = e->next;
163 if (e->next)
9
Taking false branch
164 e->next->self_p = e->self_p;
165
166 /* This indicates a deleted entry. */
167 e->self_p = 0;
168 e->next = 0;
169
170 /* Take out of the directory ordered list. */
171 ordered_unlink (e);
172
173 /* If there's a node attached, we'll delete the entry whenever it goes
174 away, otherwise, just delete it now. */
175 if (! e->node)
10
Taking true branch
176 free_entry (e);
11
Calling 'free_entry'
14
Returned released memory via 1st parameter
177}
178
179/* Clear the valid bit in all DIR's htable. */
180static void
181mark (struct ftpfs_dir *dir)
182{
183 size_t len = dir->htable_len, i;
184 struct ftpfs_dir_entry **htable = dir->htable, *e;
185
186 for (i = 0; i < len; i++)
187 for (e = htable[i]; e; e = e->next)
188 e->valid = 0;
189}
190
191/* Delete any entries in DIR which don't have their valid bit set. */
192static void
193sweep (struct ftpfs_dir *dir)
194{
195 size_t len = dir->htable_len, i;
196 struct ftpfs_dir_entry **htable = dir->htable, *e;
197
198 for (i = 0; i < len; i++)
2
Assuming 'i' is < 'len'
3
Loop condition is true. Entering loop body
199 for (e = htable[i]; e; e = e->next)
4
Loop condition is true. Entering loop body
5
Loop condition is true. Entering loop body
16
Use of memory after it is freed
200 if (!e->valid && !e->noent)
6
Taking true branch
201 delete (e, dir);
7
Calling 'delete'
15
Returned released memory via 1st parameter
202}
203
204/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
205 True is returned if successful, or false if there was a memory allocation
206 error. TIMESTAMP is used to record the time of this update. */
207static void
208update_entry (struct ftpfs_dir_entry *e, const struct stat *st,
209 const char *symlink_target, time_t timestamp)
210{
211 ino_t ino;
212 struct ftpfs *fs = e->dir->fs;
213
214 if (e->stat.st_ino)
215 ino = e->stat.st_ino;
216 else
217 ino = fs->next_inode++;
218
219 e->name_timestamp = timestamp;
220
221 if (st)
222 /* The ST and SYMLINK_TARGET parameters are only valid if ST isn't 0. */
223 {
224 e->stat = *st;
225 e->stat_timestamp = timestamp;
226
227 if (!e->symlink_target || !symlink_target
228 || strcmp (e->symlink_target, symlink_target) != 0)
229 {
230 if (e->symlink_target)
231 free (e->symlink_target);
232 e->symlink_target = symlink_target ? strdup (symlink_target) : 0;
233 }
234 }
235
236 /* The st_ino field is always valid. */
237 e->stat.st_ino = ino;
238 e->stat.st_fsid = fs->fsid;
239 e->stat.st_fstype = FSTYPE_FTP0x00000005;
240}
241
242/* Add the timestamp TIMESTAMP to the set used to detect bulk stats, and
243 return true if there have been enough individual stats recently to call
244 for just refetching the whole directory. */
245static int
246need_bulk_stat (time_t timestamp, struct ftpfs_dir *dir)
247{
248 time_t period = dir->fs->params.bulk_stat_period;
249 unsigned threshold = dir->fs->params.bulk_stat_threshold;
250
251 if (timestamp > dir->bulk_stat_base_stamp + period * 3)
252 /* No stats done in a while, just start over. */
253 {
254 dir->bulk_stat_count_first_half = 1;
255 dir->bulk_stat_count_second_half = 0;
256 dir->bulk_stat_base_stamp = (timestamp / period) * period;
257 }
258 else if (timestamp > dir->bulk_stat_base_stamp + period * 2)
259 /* Start a new period, but keep the second half of the old one. */
260 {
261 dir->bulk_stat_count_first_half = dir->bulk_stat_count_second_half;
262 dir->bulk_stat_count_second_half = 1;
263 dir->bulk_stat_base_stamp += period;
264 }
265 else if (timestamp > dir->bulk_stat_base_stamp + period)
266 dir->bulk_stat_count_second_half++;
267 else
268 dir->bulk_stat_count_first_half++;
269
270 return
271 (dir->bulk_stat_count_first_half + dir->bulk_stat_count_second_half)
272 > threshold;
273}
274
275static void
276reset_bulk_stat_info (struct ftpfs_dir *dir)
277{
278 dir->bulk_stat_count_first_half = 0;
279 dir->bulk_stat_count_second_half = 0;
280 dir->bulk_stat_base_stamp = 0;
281}
282
283/* State shared between ftpfs_dir_refresh and update_ordered_entry. */
284struct dir_fetch_state
285{
286 struct ftpfs_dir *dir;
287 time_t timestamp;
288
289 /* A pointer to the NEXT-field of the previously seen entry, or a pointer
290 to the ORDERED field in the directory if this is the first. */
291 struct ftpfs_dir_entry **prev_entry_next_p;
292};
293
294/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET, also
295 rearranging the entries to reflect the order in which they are sent from
296 the server, and setting their valid bits so that obsolete entries can be
297 deleted. HOOK points to the state from ftpfs_dir_fetch. */
298static error_t
299update_ordered_entry (const char *name, const struct stat *st,
300 const char *symlink_target, void *hook)
301{
302 struct dir_fetch_state *dfs = hook;
303 struct ftpfs_dir_entry *e = lookup (dfs->dir, name, 1);
304
305 if (! e)
306 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
307
308 update_entry (e, st, symlink_target, dfs->timestamp);
309 e->valid = 1;
310
311 if (! e->ordered_self_p)
312 /* Position E in the ordered chain following the previously seen entry. */
313 {
314 /* The PREV_ENTRY_NEXT_P field holds a pointer to the NEXT-field of the
315 previous entry, or a pointer to the ORDERED field in the directory. */
316 e->ordered_self_p = dfs->prev_entry_next_p;
317
318 if (*e->ordered_self_p)
319 /* Update the self_p pointer of the previous successor. */
320 (*e->ordered_self_p)->ordered_self_p = &e->ordered_next;
321
322 /* E comes before the previous successor. */
323 e->ordered_next = *e->ordered_self_p;
324
325 *e->ordered_self_p = e; /* Put E there. */
326 }
327
328 /* Put the next entry after this one. */
329 dfs->prev_entry_next_p = &e->ordered_next;
330
331 return 0;
332}
333
334/* Update the directory entry for NAME, rearranging the entries to reflect
335 the order in which they are sent from the server, and setting their valid
336 bits so that obsolete entries can be deleted. HOOK points to the state
337 from ftpfs_dir_fetch. */
338static error_t
339update_ordered_name (const char *name, void *hook)
340{
341 /* We just do the same thing as for stats, but without the stat info. */
342 return update_ordered_entry (name, 0, 0, hook);
343}
344
345/* Refresh DIR from the directory DIR_NAME in the filesystem FS. If
346 UPDATE_STATS is true, then directory stat information will also be
347 updated. If PRESERVE_ENTRY is non-0, that entry won't be deleted if it's
348 not in the directory after the refresh, but instead will have its NOENT
349 flag turned on. */
350static error_t
351refresh_dir (struct ftpfs_dir *dir, int update_stats, time_t timestamp,
352 struct ftpfs_dir_entry *preserve_entry)
353{
354 error_t err;
355 struct ftp_conn *conn;
356 struct dir_fetch_state dfs;
357
358 if ((update_stats
359 ? dir->stat_timestamp + dir->fs->params.stat_timeout
360 : dir->name_timestamp + dir->fs->params.name_timeout)
361 >= timestamp)
362 /* We've already refreshed this directory recently. */
363 return 0;
364
365 err = ftpfs_get_ftp_conn (dir->fs, &conn);
366 if (err)
367 return err;
368
369 /* Mark directory entries so we can GC them later using sweep. */
370 mark (dir);
371
372 if (update_stats)
373 /* We're doing a bulk stat now, so don't do another for a while. */
374 reset_bulk_stat_info (dir);
375
376 /* Info passed to update_ordered_entry. */
377 dfs.dir = dir;
378 dfs.timestamp = timestamp;
379 dfs.prev_entry_next_p = &dir->ordered;
380
381 /* Make sure `.' and `..' are always included (if the actual list also
382 includes `.' and `..', the ordered may be rearranged). */
383 err = update_ordered_name (".", &dfs);
384 if (! err)
385 err = update_ordered_name ("..", &dfs);
386
387 if (! err)
388 {
389 /* Refetch the directory from the server. */
390 if (update_stats)
391 /* Fetch both names and stat info. */
392 err = ftp_conn_get_stats (conn, dir->rmt_path, 1,
393 update_ordered_entry, &dfs);
394 else
395 /* Just fetch names. */
396 err = ftp_conn_get_names (conn, dir->rmt_path,
397 update_ordered_name, &dfs);
398 }
399
400 if (! err)
401 /* GC any directory entries that weren't seen this time. */
402 {
403 dir->name_timestamp = timestamp;
404 if (update_stats)
405 dir->stat_timestamp = timestamp;
406 if (preserve_entry && !preserve_entry->valid)
407 {
408 preserve_entry->noent = 1;
409 preserve_entry->name_timestamp = timestamp;
410 }
411 sweep (dir);
412 }
413
414 ftpfs_release_ftp_conn (dir->fs, conn);
415
416 return err;
417}
418
419/* Refresh DIR. */
420error_t
421ftpfs_dir_refresh (struct ftpfs_dir *dir)
422{
423 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
424 return refresh_dir (dir, 0, timestamp, 0);
425}
426
427/* State shared between ftpfs_dir_entry_refresh and update_old_entry. */
428struct refresh_entry_state
429{
430 struct ftpfs_dir_entry *entry;
431 time_t timestamp;
432};
433
434/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
435 HOOK points to the state from ftpfs_dir_fetch_entry. */
436static error_t
437update_old_entry (const char *name, const struct stat *st,
438 const char *symlink_target, void *hook)
439{
440 struct refresh_entry_state *res = hook;
441
442 if (strcmp (name, res->entry->name) != 0)
443 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
444
445 update_entry (res->entry, st, symlink_target, res->timestamp);
446
447 return 0;
448}
449
450/* Refresh stat information for NODE. This may actually refresh the whole
451 directory if that is deemed desirable. NODE should be locked. */
452error_t
453ftpfs_refresh_node (struct node *node)
454{
455 struct netnode *nn = node->nn;
456 struct ftpfs_dir_entry *entry = nn->dir_entry;
457
458 if (! entry)
459 /* This is a deleted node, don't attempt to do anything. */
460 return 0;
461 else
462 {
463 error_t err = 0;
464 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
465 struct ftpfs_dir *dir = entry->dir;
466
467 pthread_mutex_lock (&dir->node->lock);
468
469 if (! entry->self_p)
470 /* This is a deleted entry, just awaiting disposal; do so. */
471 {
472 nn->dir_entry = 0;
473 free_entry (entry);
474 return 0;
475 }
476 else if ((entry->name_timestamp + dir->fs->params.name_timeout
477 >= timestamp)
478 && entry->noent)
479 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
480 else if (entry->stat_timestamp + dir->fs->params.stat_timeout < timestamp)
481 {
482 /* Stat information needs updating. */
483 if (need_bulk_stat (timestamp, dir))
484 /* Refetch the whole directory from the server. */
485 {
486 err = refresh_dir (entry->dir, 1, timestamp, entry);
487 if (!err && entry->noent)
488 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
489 }
490 else if (*(entry->name))
491 {
492 /* The root node is treated separately below. */
493 struct ftp_conn *conn;
494
495 err = ftpfs_get_ftp_conn (dir->fs, &conn);
496
497 if (! err)
498 {
499 char *rmt_path;
500
501 err = ftp_conn_append_name (conn, dir->rmt_path, entry->name,
502 &rmt_path);
503 if (! err)
504 {
505 struct refresh_entry_state res;
506
507 res.entry = entry;
508 res.timestamp = timestamp;
509
510 if (! err)
511 err = ftp_conn_get_stats (conn, rmt_path, 0,
512 update_old_entry, &res);
513
514 free (rmt_path);
515 }
516
517 ftpfs_release_ftp_conn (dir->fs, conn);
518 }
519
520 if (err == ENOENT((0x10 << 26) | ((2) & 0x3fff)))
521 {
522 entry->noent = 1; /* A negative entry. */
523 entry->name_timestamp = timestamp;
524 }
525 }
526 else
527 {
528 /* Refresh the root node with the old stat
529 information. */
530 struct refresh_entry_state res;
531 res.entry = entry;
532 res.timestamp = timestamp;
533 err = update_old_entry (entry->name,
534 &netfs_root_node->nn_stat,
535 NULL((void*)0), &res);
536 }
537 }
538
539 if ((entry->stat.st_mtim.tv_sec < node->nn_stat.st_mtim.tv_sec
540 || (entry->stat.st_mtim.tv_sec == node->nn_stat.st_mtim.tv_sec
541 && entry->stat.st_mtim.tv_nsec < node->nn_stat.st_mtim.tv_nsec)
542 || entry->stat.st_size != node->nn_stat.st_size)
543 && nn && nn->contents)
544 /* The file has changed. */
545 ccache_invalidate (nn->contents);
546
547 node->nn_stat = entry->stat;
548 node->nn_translated = S_ISLNK (entry->stat.st_mode)((((entry->stat.st_mode)) & 0170000) == (0120000)) ? S_IFLNK0120000 : 0;
549 if (!nn->dir && S_ISDIR (entry->stat.st_mode)((((entry->stat.st_mode)) & 0170000) == (0040000)))
550 ftpfs_dir_create (nn->fs, node, nn->rmt_path, &nn->dir);
551
552 pthread_mutex_unlock (&dir->node->lock);
553
554 ftpfs_cache_node (node);
555
556 return err;
557 }
558}
559
560/* Remove NODE from its entry (if the entry is still valid, it will remain
561 without a node). NODE should be locked. */
562error_t
563ftpfs_detach_node (struct node *node)
564{
565 struct netnode *nn = node->nn;
566 struct ftpfs_dir_entry *entry = nn->dir_entry;
567
568 if (entry)
569 /* NODE is still attached to some entry, so detach it. */
570 {
571 struct ftpfs_dir *dir = entry->dir;
572
573 pthread_mutex_lock (&dir->node->lock);
574
575 if (entry->self_p)
576 /* Just detach NODE from the still active entry. */
577 entry->node = 0;
578 else
579 /* This is a deleted entry, just awaiting disposal; do so. */
580 {
581 nn->dir_entry = 0;
582 free_entry (entry);
583 }
584
585 if (--dir->num_live_entries == 0)
586 netfs_nput (dir->node);
587 else
588 pthread_mutex_unlock (&dir->node->lock);
589 }
590
591 return 0;
592}
593
594/* State shared between ftpfs_dir_lookup and update_new_entry. */
595struct new_entry_state
596{
597 time_t timestamp;
598 struct ftpfs_dir *dir;
599 struct ftpfs_dir_entry *entry;
600};
601
602/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
603 HOOK->entry will be updated to reflect the new entry. */
604static error_t
605update_new_entry (const char *name, const struct stat *st,
606 const char *symlink_target, void *hook)
607{
608 struct ftpfs_dir_entry *e;
609 struct new_entry_state *nes = hook;
610
611 e = lookup (nes->dir, name, 1);
612 if (! e)
613 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
614
615 update_entry (e, st, symlink_target, nes->timestamp);
616 nes->entry = e;
617
618 return 0;
619}
620
621/* Lookup NAME in DIR, returning its entry, or an error. DIR's node should
622 be locked, and will be unlocked after returning; *NODE will contain the
623 result node, locked, and with an additional reference, or 0 if an error
624 occurs. */
625error_t
626ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
627 struct node **node)
628{
629 struct ftp_conn *conn;
630 struct ftpfs_dir_entry *e;
631 error_t err = 0;
632 char *rmt_path = 0;
633 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
634
635 if (*name == '\0' || strcmp (name, ".") == 0)
636 /* Current directory -- just add an additional reference to DIR's node
637 and return it. */
638 {
639 netfs_nref (dir->node);
640 *node = dir->node;
641 return 0;
642 }
643 else if (strcmp (name, "..") == 0)
644 /* Parent directory. */
645 {
646 if (dir->node->nn->dir_entry)
647 {
648 *node = dir->node->nn->dir_entry->dir->node;
649 pthread_mutex_lock (&(*node)->lock);
650 netfs_nref (*node);
651 }
652 else
653 {
654 err = ENOENT((0x10 << 26) | ((2) & 0x3fff)); /* No .. */
655 *node = 0;
656 }
657
658 pthread_mutex_unlock (&dir->node->lock);
659
660 return err;
661 }
662
663 e = lookup (dir, name, 0);
664 if (!e || e->name_timestamp + dir->fs->params.name_timeout < timestamp)
665 /* Try to fetch info about NAME. */
666 {
667 if (need_bulk_stat (timestamp, dir))
668 /* Refetch the whole directory from the server. */
669 {
670 err = refresh_dir (dir, 1, timestamp, e);
671 if (!err && !e)
672 e = lookup (dir, name, 0);
673 }
674 else
675 {
676 err = ftpfs_get_ftp_conn (dir->fs, &conn);
677 if (! err)
678 {
679 err = ftp_conn_append_name (conn, dir->rmt_path, name,
680 &rmt_path);
681 if (! err)
682 {
683 struct new_entry_state nes;
684
685 nes.dir = dir;
686 nes.timestamp = timestamp;
687 nes.entry = NULL((void*)0);
688
689 err = ftp_conn_get_stats (conn, rmt_path, 0,
690 update_new_entry, &nes);
691 if (! err)
692 e = nes.entry;
693 else if (err == ENOENT((0x10 << 26) | ((2) & 0x3fff)))
694 {
695 e = lookup (dir, name, 1);
696 if (! e)
697 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
698 else
699 {
700 e->noent = 1; /* A negative entry. */
701 e->name_timestamp = timestamp;
702 }
703 }
704 }
705
706 ftpfs_release_ftp_conn (dir->fs, conn);
707 }
708 }
709 }
710
711 if (! err)
712 {
713 if (e && !e->noent)
714 /* We've got a dir entry, get a node for it. */
715 {
716 /* If there's already a node, add a ref so that it doesn't go
717 away. */
718 pthread_spin_lock (&netfs_node_refcnt_lock);
719 if (e->node)
720 e->node->references++;
721 pthread_spin_unlock (&netfs_node_refcnt_lock);
722
723 if (! e->node)
724 /* No node; make one and install it into E. */
725 {
726 if (! rmt_path)
727 /* We have to cons up the absolute path. We need the
728 connection just for the pathname frobbing functions. */
729 {
730 err = ftpfs_get_ftp_conn (dir->fs, &conn);
731 if (! err)
732 {
733 err = ftp_conn_append_name (conn, dir->rmt_path, name,
734 &rmt_path);
735 ftpfs_release_ftp_conn (dir->fs, conn);
736 }
737 }
738
739 if (! err)
740 {
741 err = ftpfs_create_node (e, rmt_path, &e->node);
742
743 if (!err && dir->num_live_entries++ == 0)
744 /* Keep a reference to dir's node corresponding to
745 children. */
746 {
747 pthread_spin_lock (&netfs_node_refcnt_lock);
748 dir->node->references++;
749 pthread_spin_unlock (&netfs_node_refcnt_lock);
750 }
751 }
752 }
753
754 if (! err)
755 {
756 *node = e->node;
757 /* We have to unlock DIR's node before locking the child node
758 because the locking order is always child-parent. We know
759 the child node won't go away because we already hold the
760 additional reference to it. */
761 pthread_mutex_unlock (&dir->node->lock);
762 pthread_mutex_lock (&e->node->lock);
763 }
764 }
765 else
766 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
767 }
768
769 if (err)
770 {
771 *node = 0;
772 pthread_mutex_unlock (&dir->node->lock);
773 }
774
775 if (rmt_path)
776 free (rmt_path);
777
778 return err;
779}
780
781/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
782 ftpfs_dir_lookup, this won't attempt to validate the existence of the
783 entry (to avoid opening a new connection if possible) -- that will happen
784 the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
785 function doesn't expect DIR to be locked, and won't return *NODE locked.
786 This function is only used for bootstrapping the root node. */
787error_t
788ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node)
789{
790 struct ftpfs_dir_entry *e;
791 error_t err = 0;
792
793 e = lookup (dir, "", 1);
794 if (! e)
795 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
796
797 if (! e->noent)
798 /* We've got a dir entry, get a node for it. */
799 {
800 /* If there's already a node, add a ref so that it doesn't go away. */
801 pthread_spin_lock (&netfs_node_refcnt_lock);
802 if (e->node)
803 e->node->references++;
804 pthread_spin_unlock (&netfs_node_refcnt_lock);
805
806 if (! e->node)
807 /* No node; make one and install it into E. */
808 {
809 err = ftpfs_create_node (e, dir->rmt_path, &e->node);
810
811 if (!err && dir->num_live_entries++ == 0)
812 /* Keep a reference to dir's node corresponding to children. */
813 {
814 pthread_spin_lock (&netfs_node_refcnt_lock);
815 dir->node->references++;
816 pthread_spin_unlock (&netfs_node_refcnt_lock);
817 }
818 }
819
820 if (! err)
821 *node = e->node;
822 }
823 else
824 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
825
826 return err;
827}
828
829/* Size of initial htable for a new directory. */
830#define INIT_HTABLE_LEN5 5
831
832/* Return in DIR a new ftpfs directory, in the filesystem FS, with node NODE
833 and remote path RMT_PATH. RMT_PATH is *not copied*, so it shouldn't ever
834 change while this directory is active. */
835error_t
836ftpfs_dir_create (struct ftpfs *fs, struct node *node, const char *rmt_path,
837 struct ftpfs_dir **dir)
838{
839 struct ftpfs_dir *new = malloc (sizeof (struct ftpfs_dir));
840 struct ftpfs_dir_entry **htable
841 = calloc (INIT_HTABLE_LEN5, sizeof (struct ftpfs_dir_entry *));
842
843 if (!new || !htable)
844 {
845 if (new)
846 free (new);
847 if (htable)
848 free (htable);
849 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
850 }
851
852 /* Hold a reference to the new dir's node. */
853 pthread_spin_lock (&netfs_node_refcnt_lock);
854 node->references++;
855 pthread_spin_unlock (&netfs_node_refcnt_lock);
856
857 new->num_entries = 0;
858 new->num_live_entries = 0;
859 new->htable_len = INIT_HTABLE_LEN5;
860 new->htable = htable;
861 new->ordered = 0;
862 new->rmt_path = rmt_path;
863 new->fs = fs;
864 new->node = node;
865 new->stat_timestamp = 0;
866 new->name_timestamp = 0;
867 new->bulk_stat_base_stamp = 0;
868 new->bulk_stat_count_first_half = 0;
869 new->bulk_stat_count_second_half = 0;
870
871 *dir = new;
872
873 return 0;
874}
875
876void
877ftpfs_dir_free (struct ftpfs_dir *dir)
878{
879 /* Free all entries. */
880 mark (dir);
881 sweep (dir);
1
Calling 'sweep'
882
883 if (dir->htable)
884 free (dir->htable);
885
886 netfs_nrele (dir->node);
887
888 free (dir);
889}