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 bzero (new_htable, 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 bzero (&e->stat, 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 /* Refetch the directory from the server. */
388 if (update_stats)
389 /* Fetch both names and stat info. */
390 err = ftp_conn_get_stats (conn, dir->rmt_path, 1,
391 update_ordered_entry, &dfs);
392 else
393 /* Just fetch names. */
394 err = ftp_conn_get_names (conn, dir->rmt_path, update_ordered_name, &dfs);
395
396 if (! err)
397 /* GC any directory entries that weren't seen this time. */
398 {
399 dir->name_timestamp = timestamp;
400 if (update_stats)
401 dir->stat_timestamp = timestamp;
402 if (preserve_entry && !preserve_entry->valid)
403 {
404 preserve_entry->noent = 1;
405 preserve_entry->name_timestamp = timestamp;
406 }
407 sweep (dir);
408 }
409
410 ftpfs_release_ftp_conn (dir->fs, conn);
411
412 return err;
413}
414
415/* Refresh DIR. */
416error_t
417ftpfs_dir_refresh (struct ftpfs_dir *dir)
418{
419 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
420 return refresh_dir (dir, 0, timestamp, 0);
421}
422
423/* State shared between ftpfs_dir_entry_refresh and update_old_entry. */
424struct refresh_entry_state
425{
426 struct ftpfs_dir_entry *entry;
427 time_t timestamp;
428};
429
430/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
431 HOOK points to the state from ftpfs_dir_fetch_entry. */
432static error_t
433update_old_entry (const char *name, const struct stat *st,
434 const char *symlink_target, void *hook)
435{
436 struct refresh_entry_state *res = hook;
437
438 if (strcmp (name, res->entry->name) != 0)
439 return EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff));
440
441 update_entry (res->entry, st, symlink_target, res->timestamp);
442
443 return 0;
444}
445
446/* Refresh stat information for NODE. This may actually refresh the whole
447 directory if that is deemed desirable. NODE should be locked. */
448error_t
449ftpfs_refresh_node (struct node *node)
450{
451 struct netnode *nn = node->nn;
452 struct ftpfs_dir_entry *entry = nn->dir_entry;
453
454 if (! entry)
455 /* This is a deleted node, don't attempt to do anything. */
456 return 0;
457 else
458 {
459 error_t err = 0;
460 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
461 struct ftpfs_dir *dir = entry->dir;
462
463 pthread_mutex_lock (&dir->node->lock);
464
465 if (! entry->self_p)
466 /* This is a deleted entry, just awaiting disposal; do so. */
467 {
468 nn->dir_entry = 0;
469 free_entry (entry);
470 return 0;
471 }
472 else if ((entry->name_timestamp + dir->fs->params.name_timeout
473 >= timestamp)
474 && entry->noent)
475 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
476 else if (entry->stat_timestamp + dir->fs->params.stat_timeout < timestamp)
477 {
478 /* Stat information needs updating. */
479 if (need_bulk_stat (timestamp, dir))
480 /* Refetch the whole directory from the server. */
481 {
482 err = refresh_dir (entry->dir, 1, timestamp, entry);
483 if (!err && entry->noent)
484 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
485 }
486 else if (*(entry->name))
487 {
488 /* The root node is treated separately below. */
489 struct ftp_conn *conn;
490
491 err = ftpfs_get_ftp_conn (dir->fs, &conn);
492
493 if (! err)
494 {
495 char *rmt_path;
496
497 err = ftp_conn_append_name (conn, dir->rmt_path, entry->name,
498 &rmt_path);
499 if (! err)
500 {
501 struct refresh_entry_state res;
502
503 res.entry = entry;
504 res.timestamp = timestamp;
505
506 if (! err)
507 err = ftp_conn_get_stats (conn, rmt_path, 0,
508 update_old_entry, &res);
509
510 free (rmt_path);
511 }
512
513 ftpfs_release_ftp_conn (dir->fs, conn);
514 }
515
516 if (err == ENOENT((0x10 << 26) | ((2) & 0x3fff)))
517 {
518 entry->noent = 1; /* A negative entry. */
519 entry->name_timestamp = timestamp;
520 }
521 }
522 else
523 {
524 /* Refresh the root node with the old stat
525 information. */
526 struct refresh_entry_state res;
527 res.entry = entry;
528 res.timestamp = timestamp;
529 err = update_old_entry (entry->name,
530 &netfs_root_node->nn_stat,
531 NULL((void*)0), &res);
532 }
533 }
534
535 if ((entry->stat.st_mtim.tv_sec < node->nn_stat.st_mtim.tv_sec
536 || (entry->stat.st_mtim.tv_sec == node->nn_stat.st_mtim.tv_sec
537 && entry->stat.st_mtim.tv_nsec < node->nn_stat.st_mtim.tv_nsec)
538 || entry->stat.st_size != node->nn_stat.st_size)
539 && nn && nn->contents)
540 /* The file has changed. */
541 ccache_invalidate (nn->contents);
542
543 node->nn_stat = entry->stat;
544 node->nn_translated = S_ISLNK (entry->stat.st_mode)((((entry->stat.st_mode)) & 0170000) == (0120000)) ? S_IFLNK0120000 : 0;
545 if (!nn->dir && S_ISDIR (entry->stat.st_mode)((((entry->stat.st_mode)) & 0170000) == (0040000)))
546 ftpfs_dir_create (nn->fs, node, nn->rmt_path, &nn->dir);
547
548 pthread_mutex_unlock (&dir->node->lock);
549
550 ftpfs_cache_node (node);
551
552 return err;
553 }
554}
555
556/* Remove NODE from its entry (if the entry is still valid, it will remain
557 without a node). NODE should be locked. */
558error_t
559ftpfs_detach_node (struct node *node)
560{
561 struct netnode *nn = node->nn;
562 struct ftpfs_dir_entry *entry = nn->dir_entry;
563
564 if (entry)
565 /* NODE is still attached to some entry, so detach it. */
566 {
567 struct ftpfs_dir *dir = entry->dir;
568
569 pthread_mutex_lock (&dir->node->lock);
570
571 if (entry->self_p)
572 /* Just detach NODE from the still active entry. */
573 entry->node = 0;
574 else
575 /* This is a deleted entry, just awaiting disposal; do so. */
576 {
577 nn->dir_entry = 0;
578 free_entry (entry);
579 }
580
581 if (--dir->num_live_entries == 0)
582 netfs_nput (dir->node);
583 else
584 pthread_mutex_unlock (&dir->node->lock);
585 }
586
587 return 0;
588}
589
590/* State shared between ftpfs_dir_lookup and update_new_entry. */
591struct new_entry_state
592{
593 time_t timestamp;
594 struct ftpfs_dir *dir;
595 struct ftpfs_dir_entry *entry;
596};
597
598/* Update the directory entry for NAME to reflect ST and SYMLINK_TARGET.
599 HOOK->entry will be updated to reflect the new entry. */
600static error_t
601update_new_entry (const char *name, const struct stat *st,
602 const char *symlink_target, void *hook)
603{
604 struct ftpfs_dir_entry *e;
605 struct new_entry_state *nes = hook;
606
607 e = lookup (nes->dir, name, 1);
608 if (! e)
609 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
610
611 update_entry (e, st, symlink_target, nes->timestamp);
612 nes->entry = e;
613
614 return 0;
615}
616
617/* Lookup NAME in DIR, returning its entry, or an error. DIR's node should
618 be locked, and will be unlocked after returning; *NODE will contain the
619 result node, locked, and with an additional reference, or 0 if an error
620 occurs. */
621error_t
622ftpfs_dir_lookup (struct ftpfs_dir *dir, const char *name,
623 struct node **node)
624{
625 struct ftp_conn *conn;
626 struct ftpfs_dir_entry *e;
627 error_t err = 0;
628 char *rmt_path = 0;
629 time_t timestamp = NOW({ struct timeval tv; maptime_read (ftpfs_maptime, &tv); tv
.tv_sec; })
;
630
631 if (*name == '\0' || strcmp (name, ".") == 0)
632 /* Current directory -- just add an additional reference to DIR's node
633 and return it. */
634 {
635 netfs_nref (dir->node);
636 *node = dir->node;
637 return 0;
638 }
639 else if (strcmp (name, "..") == 0)
640 /* Parent directory. */
641 {
642 if (dir->node->nn->dir_entry)
643 {
644 *node = dir->node->nn->dir_entry->dir->node;
645 pthread_mutex_lock (&(*node)->lock);
646 netfs_nref (*node);
647 }
648 else
649 {
650 err = ENOENT((0x10 << 26) | ((2) & 0x3fff)); /* No .. */
651 *node = 0;
652 }
653
654 pthread_mutex_unlock (&dir->node->lock);
655
656 return err;
657 }
658
659 e = lookup (dir, name, 0);
660 if (!e || e->name_timestamp + dir->fs->params.name_timeout < timestamp)
661 /* Try to fetch info about NAME. */
662 {
663 if (need_bulk_stat (timestamp, dir))
664 /* Refetch the whole directory from the server. */
665 {
666 err = refresh_dir (dir, 1, timestamp, e);
667 if (!err && !e)
668 e = lookup (dir, name, 0);
669 }
670 else
671 {
672 err = ftpfs_get_ftp_conn (dir->fs, &conn);
673 if (! err)
674 {
675 err = ftp_conn_append_name (conn, dir->rmt_path, name,
676 &rmt_path);
677 if (! err)
678 {
679 struct new_entry_state nes;
680
681 nes.dir = dir;
682 nes.timestamp = timestamp;
683 nes.entry = NULL((void*)0);
684
685 err = ftp_conn_get_stats (conn, rmt_path, 0,
686 update_new_entry, &nes);
687 if (! err)
688 e = nes.entry;
689 else if (err == ENOENT((0x10 << 26) | ((2) & 0x3fff)))
690 {
691 e = lookup (dir, name, 1);
692 if (! e)
693 err = ENOMEM((0x10 << 26) | ((12) & 0x3fff));
694 else
695 {
696 e->noent = 1; /* A negative entry. */
697 e->name_timestamp = timestamp;
698 }
699 }
700 }
701
702 ftpfs_release_ftp_conn (dir->fs, conn);
703 }
704 }
705 }
706
707 if (! err)
708 {
709 if (e && !e->noent)
710 /* We've got a dir entry, get a node for it. */
711 {
712 /* If there's already a node, add a ref so that it doesn't go
713 away. */
714 pthread_spin_lock (&netfs_node_refcnt_lock);
715 if (e->node)
716 e->node->references++;
717 pthread_spin_unlock (&netfs_node_refcnt_lock);
718
719 if (! e->node)
720 /* No node; make one and install it into E. */
721 {
722 if (! rmt_path)
723 /* We have to cons up the absolute path. We need the
724 connection just for the pathname frobbing functions. */
725 {
726 err = ftpfs_get_ftp_conn (dir->fs, &conn);
727 if (! err)
728 {
729 err = ftp_conn_append_name (conn, dir->rmt_path, name,
730 &rmt_path);
731 ftpfs_release_ftp_conn (dir->fs, conn);
732 }
733 }
734
735 if (! err)
736 {
737 err = ftpfs_create_node (e, rmt_path, &e->node);
738
739 if (!err && dir->num_live_entries++ == 0)
740 /* Keep a reference to dir's node corresponding to
741 children. */
742 {
743 pthread_spin_lock (&netfs_node_refcnt_lock);
744 dir->node->references++;
745 pthread_spin_unlock (&netfs_node_refcnt_lock);
746 }
747 }
748 }
749
750 if (! err)
751 {
752 *node = e->node;
753 /* We have to unlock DIR's node before locking the child node
754 because the locking order is always child-parent. We know
755 the child node won't go away because we already hold the
756 additional reference to it. */
757 pthread_mutex_unlock (&dir->node->lock);
758 pthread_mutex_lock (&e->node->lock);
759 }
760 }
761 else
762 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
763 }
764
765 if (err)
766 {
767 *node = 0;
768 pthread_mutex_unlock (&dir->node->lock);
769 }
770
771 if (rmt_path)
772 free (rmt_path);
773
774 return err;
775}
776
777/* Lookup the null name in DIR, and return a node for it in NODE. Unlike
778 ftpfs_dir_lookup, this won't attempt to validate the existence of the
779 entry (to avoid opening a new connection if possible) -- that will happen
780 the first time the entry is refreshed. Also unlink ftpfs_dir_lookup, this
781 function doesn't expect DIR to be locked, and won't return *NODE locked.
782 This function is only used for bootstrapping the root node. */
783error_t
784ftpfs_dir_null_lookup (struct ftpfs_dir *dir, struct node **node)
785{
786 struct ftpfs_dir_entry *e;
787 error_t err = 0;
788
789 e = lookup (dir, "", 1);
790 if (! e)
791 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
792
793 if (! e->noent)
794 /* We've got a dir entry, get a node for it. */
795 {
796 /* If there's already a node, add a ref so that it doesn't go away. */
797 pthread_spin_lock (&netfs_node_refcnt_lock);
798 if (e->node)
799 e->node->references++;
800 pthread_spin_unlock (&netfs_node_refcnt_lock);
801
802 if (! e->node)
803 /* No node; make one and install it into E. */
804 {
805 err = ftpfs_create_node (e, dir->rmt_path, &e->node);
806
807 if (!err && dir->num_live_entries++ == 0)
808 /* Keep a reference to dir's node corresponding to children. */
809 {
810 pthread_spin_lock (&netfs_node_refcnt_lock);
811 dir->node->references++;
812 pthread_spin_unlock (&netfs_node_refcnt_lock);
813 }
814 }
815
816 if (! err)
817 *node = e->node;
818 }
819 else
820 err = ENOENT((0x10 << 26) | ((2) & 0x3fff));
821
822 return err;
823}
824
825/* Size of initial htable for a new directory. */
826#define INIT_HTABLE_LEN5 5
827
828/* Return in DIR a new ftpfs directory, in the filesystem FS, with node NODE
829 and remote path RMT_PATH. RMT_PATH is *not copied*, so it shouldn't ever
830 change while this directory is active. */
831error_t
832ftpfs_dir_create (struct ftpfs *fs, struct node *node, const char *rmt_path,
833 struct ftpfs_dir **dir)
834{
835 struct ftpfs_dir *new = malloc (sizeof (struct ftpfs_dir));
836 struct ftpfs_dir_entry **htable
837 = calloc (INIT_HTABLE_LEN5, sizeof (struct ftpfs_dir_entry *));
838
839 if (!new || !htable)
840 {
841 if (new)
842 free (new);
843 if (htable)
844 free (htable);
845 return ENOMEM((0x10 << 26) | ((12) & 0x3fff));
846 }
847
848 /* Hold a reference to the new dir's node. */
849 pthread_spin_lock (&netfs_node_refcnt_lock);
850 node->references++;
851 pthread_spin_unlock (&netfs_node_refcnt_lock);
852
853 new->num_entries = 0;
854 new->num_live_entries = 0;
855 new->htable_len = INIT_HTABLE_LEN5;
856 new->htable = htable;
857 new->ordered = 0;
858 new->rmt_path = rmt_path;
859 new->fs = fs;
860 new->node = node;
861 new->stat_timestamp = 0;
862 new->name_timestamp = 0;
863 new->bulk_stat_base_stamp = 0;
864 new->bulk_stat_count_first_half = 0;
865 new->bulk_stat_count_second_half = 0;
866
867 *dir = new;
868
869 return 0;
870}
871
872void
873ftpfs_dir_free (struct ftpfs_dir *dir)
874{
875 /* Free all entries. */
876 mark (dir);
877 sweep (dir);
1
Calling 'sweep'
878
879 if (dir->htable)
880 free (dir->htable);
881
882 netfs_nrele (dir->node);
883
884 free (dir);
885}