readdir was rewritten in Ruby just before the end of the hackfest
[hurd/trovefs.git] / dir.c
1 /*
2  * Copyright (C) 1997,98,2002 Free Software Foundation, Inc.
3  * Copyright (C) 2013  Steven McDonald <steven@steven-mcdonald.id.au>
4  *
5  * ftpfs translator written by Miles Bader <miles@gnu.org>
6  * Modified for trovefs by Steven McDonald <steven@steven-mcdonald.id.au>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This software is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  */
22
23 #include <error.h>
24 #include <hurd/netfs.h>
25
26 #include "trovefs.h"
27
28 #define INIT_HTABLE_LEN 5
29
30 struct dir_fetch_state
31 {
32         struct trovefs_dir *dir;
33         struct trovefs_dir_entry **prev_entry_next_p;
34 };
35
36 static void
37 insert (struct trovefs_dir_entry *e, struct trovefs_dir_entry **htable, size_t htable_len)
38 {
39         struct trovefs_dir_entry **t = &htable[e->hv % htable_len];
40         if (*t)
41                 (*t)->self_p = &e->next;
42         e->next = *t;
43         e->self_p = t;
44         *t = e;
45 }
46
47 static error_t
48 rehash (struct trovefs_dir *dir, size_t new_len)
49 {
50         int i;
51         size_t old_len = dir->htable_len;
52         struct trovefs_dir_entry **old_htable = dir->htable;
53         struct trovefs_dir_entry **new_htable = malloc (new_len * sizeof (struct trovefs_dir_entry *));
54
55         if (! new_htable)
56                 return ENOMEM;
57
58         bzero (new_htable, new_len * sizeof (struct trovefs_dir_entry *));
59
60         for (i = 0; i < old_len; i++)
61                 while (old_htable[i])
62                 {
63                         struct trovefs_dir_entry *e = old_htable[i];
64                         old_htable[i] = e->next;
65                         insert (e, new_htable, new_len);
66                 }
67
68         free (old_htable);
69
70         dir->htable = new_htable;
71         dir->htable_len = new_len;
72
73         return 0;
74 }
75
76 static size_t
77 hash (const char *name)
78 {
79         size_t hv = 0;
80         while (*name)
81                 hv = ((hv << 5) + *name++) & 0xFFFFFF;
82         return hv;
83 }
84
85 struct trovefs_dir_entry *
86 lookup (struct trovefs_dir *dir, const char *name, int add)
87 {
88         size_t hv = hash (name);
89         struct trovefs_dir_entry *h = dir->htable[hv % dir->htable_len], *e = h;
90
91         while (e && strcmp (name, e->name) != 0)
92                 e = e->next;
93
94         if (!e && add)
95         {
96                 if (dir->num_entries > dir->htable_len)
97                         if (rehash(dir, (dir->htable_len + 1) * 2 - 1) != 0)
98                                 return 0;
99
100                 e = malloc (sizeof *e);
101                 if (e)
102                 {
103                         e->hv = hv;
104                         e->name = strdup (name);
105                         e->node = 0;
106                         e->dir = dir;
107                         e->stat_timestamp = 0;
108                         bzero (&e->stat, sizeof (e->stat));
109                         e->noent = 0;
110                         e->valid = 0;
111                         e->name_timestamp = e->stat_timestamp = 0;
112                         e->ordered_next = 0;
113                         e->ordered_self_p = 0;
114                         e->next = 0;
115                         e->self_p = 0;
116                         insert (e, dir->htable, dir->htable_len);
117                         dir->num_entries++;
118                 }
119         }
120
121         return e;
122 }
123
124 error_t
125 trovefs_dir_create (struct trovefs *fs, struct node *node, const char *rmt_path, struct trovefs_dir **dir)
126 {
127         struct trovefs_dir *new = malloc (sizeof (struct trovefs_dir));
128         struct trovefs_dir_entry **htable = calloc (INIT_HTABLE_LEN, sizeof (struct trovefs_dir_entry *));
129
130         if (!new || !htable)
131         {
132                 if (new)
133                         free (new);
134                 if (htable)
135                         free (htable);
136                 return ENOMEM;
137         }
138
139         pthread_spin_lock (&netfs_node_refcnt_lock);
140         node->references++;
141         pthread_spin_unlock (&netfs_node_refcnt_lock);
142
143         new->num_entries = 0;
144         new->num_live_entries = 0;
145         new->htable_len = INIT_HTABLE_LEN;
146         new->htable = htable;
147         new->ordered = 0;
148         new->rmt_path = rmt_path;
149         new->fs = fs;
150         new->node = node;
151         new->stat_timestamp = 0;
152         new->name_timestamp = 0;
153         new->bulk_stat_base_stamp = 0;
154         new->bulk_stat_count_first_half = 0;
155         new->bulk_stat_count_second_half = 0;
156
157         *dir = new;
158
159         return 0;
160 }
161
162 error_t
163 trovefs_dir_null_lookup (struct trovefs_dir *dir, struct node **node)
164 {
165         struct trovefs_dir_entry *e;
166         error_t err = 0;
167
168         e = lookup (dir, "", 1);
169         if (! e)
170                 return ENOMEM;
171
172         if (! e->noent)
173         {
174                 pthread_spin_lock (&netfs_node_refcnt_lock);
175                 if (e->node)
176                         e->node->references++;
177                 pthread_spin_unlock (&netfs_node_refcnt_lock);
178
179                 if (! e->node)
180                 {
181                         err = trovefs_create_node (e, dir->rmt_path, &e->node);
182
183                         if (!err && dir->num_live_entries++ == 0)
184                         {
185                                 pthread_spin_lock (&netfs_node_refcnt_lock);
186                                 dir->node->references++;
187                                 pthread_spin_unlock (&netfs_node_refcnt_lock);
188                         }
189                 }
190
191                 if (! err)
192                         *node = e->node;
193         }
194         else
195                 err = ENOENT;
196
197         return err;
198 }
199
200 error_t
201 trovefs_refresh_node (struct node *node)
202 {
203         error_t err = 0;
204         struct netnode *nn = node->nn;
205         struct trovefs_dir_entry *entry = nn->dir_entry;
206
207         if (! entry)
208                 return 0;
209         else
210         {
211                 pthread_mutex_lock (&entry->dir->node->lock);
212
213                 if (! entry->self_p)
214                 {
215                         nn->dir_entry = 0;
216                         free_entry (entry);
217                         pthread_mutex_unlock (&entry->dir->node->lock);
218                         return 0;
219                 }
220                 else
221                 {
222                         err = trovefs_s3_get_names (entry->dir);
223                         if (!err && entry->noent)
224                                 err = ENOENT;
225                 }
226
227                 if (err == ENOENT)
228                 {
229                         entry->noent = 1;
230                 }
231
232                 pthread_mutex_unlock (&entry->dir->node->lock);
233         }
234
235         return err;
236 }
237
238 void
239 free_entry (struct trovefs_dir_entry *e)
240 {
241         assert (! e->self_p);
242         free (e->name);
243         free (e);
244 }