diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-06-06 11:44:47 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-06-06 11:44:47 +0000 |
commit | 71eacc6a3ee0003df1485d15c44e9b7306105d48 (patch) | |
tree | 207716a9c0598d777c13e1fe053820ad96013c38 | |
parent | 262e08b8d54f9dae5709509d766221f31e9ebaf9 (diff) | |
download | gnunet-fuse-71eacc6a3ee0003df1485d15c44e9b7306105d48.tar.gz gnunet-fuse-71eacc6a3ee0003df1485d15c44e9b7306105d48.zip |
finishing read-only implementation of fuse for 0.9.3
-rw-r--r-- | src/fuse/getattr.c | 5 | ||||
-rw-r--r-- | src/fuse/gnunet-fuse.c | 302 | ||||
-rw-r--r-- | src/fuse/gnunet-fuse.h | 95 | ||||
-rw-r--r-- | src/fuse/open.c | 7 | ||||
-rw-r--r-- | src/fuse/read.c | 59 | ||||
-rw-r--r-- | src/fuse/readdir.c | 148 |
6 files changed, 385 insertions, 231 deletions
diff --git a/src/fuse/getattr.c b/src/fuse/getattr.c index 9bf1c9c..abf5faa 100644 --- a/src/fuse/getattr.c +++ b/src/fuse/getattr.c | |||
@@ -43,10 +43,11 @@ int | |||
43 | gn_getattr (const char *path, struct stat *stbuf) | 43 | gn_getattr (const char *path, struct stat *stbuf) |
44 | { | 44 | { |
45 | struct GNUNET_FUSE_PathInfo *pi; | 45 | struct GNUNET_FUSE_PathInfo *pi; |
46 | int eno; | ||
46 | 47 | ||
47 | pi = GNUNET_FUSE_path_info_get (path); | 48 | pi = GNUNET_FUSE_path_info_get (path, &eno); |
48 | if (NULL == pi) | 49 | if (NULL == pi) |
49 | return - ENOENT; | 50 | return - eno; |
50 | *stbuf = pi->stbuf; | 51 | *stbuf = pi->stbuf; |
51 | GNUNET_FUSE_path_info_done (pi); | 52 | GNUNET_FUSE_path_info_done (pi); |
52 | return 0; | 53 | return 0; |
diff --git a/src/fuse/gnunet-fuse.c b/src/fuse/gnunet-fuse.c index a0b43de..69c226c 100644 --- a/src/fuse/gnunet-fuse.c +++ b/src/fuse/gnunet-fuse.c | |||
@@ -25,6 +25,7 @@ | |||
25 | * @author Mauricio Günther | 25 | * @author Mauricio Günther |
26 | */ | 26 | */ |
27 | #include "gnunet-fuse.h" | 27 | #include "gnunet-fuse.h" |
28 | #include "gfs_download.h" | ||
28 | 29 | ||
29 | /** | 30 | /** |
30 | * Anonymity level to use. | 31 | * Anonymity level to use. |
@@ -57,35 +58,181 @@ static char *source; | |||
57 | static char *directory; | 58 | static char *directory; |
58 | 59 | ||
59 | /** | 60 | /** |
60 | * Global mapping of paths to GNUnet URIs (and file names) for | 61 | * Root of the file tree. |
61 | * the respective entries. | ||
62 | */ | 62 | */ |
63 | static struct GNUNET_CONTAINER_MultiHashMap *map; | 63 | static struct GNUNET_FUSE_PathInfo *root; |
64 | |||
64 | 65 | ||
65 | /** | 66 | /** |
66 | * Mutex for synchronizing access to 'map'. | 67 | * Function used to process entries in a directory; adds the |
68 | * respective entry to the parent directory. | ||
69 | * | ||
70 | * @param cls closure with the 'struct GNUNET_FUSE_PathInfo' of the parent | ||
71 | * @param filename name of the file in the directory | ||
72 | * @param uri URI of the file | ||
73 | * @param metadata metadata for the file; metadata for | ||
74 | * the directory if everything else is NULL/zero | ||
75 | * @param length length of the available data for the file | ||
76 | * (of type size_t since data must certainly fit | ||
77 | * into memory; if files are larger than size_t | ||
78 | * permits, then they will certainly not be | ||
79 | * embedded with the directory itself). | ||
80 | * @param data data available for the file (length bytes) | ||
67 | */ | 81 | */ |
68 | static struct GNUNET_Mutex *map_mutex; | 82 | static void |
83 | process_directory_entry (void *cls, | ||
84 | const char *filename, | ||
85 | const struct GNUNET_FS_Uri * | ||
86 | uri, | ||
87 | const struct | ||
88 | GNUNET_CONTAINER_MetaData * | ||
89 | meta, size_t length, | ||
90 | const void *data) | ||
91 | { | ||
92 | struct GNUNET_FUSE_PathInfo *parent = cls; | ||
93 | struct GNUNET_FUSE_PathInfo *pi; | ||
94 | int is_directory; | ||
95 | |||
96 | if (NULL == filename) | ||
97 | return; /* info about the directory itself */ | ||
98 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
99 | "Adding file `%s' to directory `%s'\n", | ||
100 | filename, | ||
101 | parent->filename); | ||
102 | is_directory = GNUNET_FS_meta_data_test_for_directory (meta); | ||
103 | if (GNUNET_SYSERR == is_directory) | ||
104 | is_directory = GNUNET_NO; /* if in doubt, say no */ | ||
105 | pi = GNUNET_FUSE_path_info_create (parent, filename, uri, is_directory); | ||
106 | GNUNET_FUSE_path_info_done (pi); | ||
107 | } | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Load and parse a directory. | ||
112 | * | ||
113 | * @param pi path to the directory | ||
114 | * @param eno where to store 'errno' on errors | ||
115 | * @return GNUNET_OK on success | ||
116 | */ | ||
117 | int | ||
118 | GNUNET_FUSE_load_directory (struct GNUNET_FUSE_PathInfo *pi, | ||
119 | int * eno) | ||
120 | { | ||
121 | size_t size; | ||
122 | void *data; | ||
123 | struct GNUNET_DISK_MapHandle *mh; | ||
124 | struct GNUNET_DISK_FileHandle *fh; | ||
125 | |||
126 | /* Need to download directory; store to temporary file */ | ||
127 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
128 | "Downloading directory `%s'\n", | ||
129 | pi->filename); | ||
130 | pi->tmpfile = GNUNET_DISK_mktemp ("gnunet-fuse-tempfile"); | ||
131 | if (GNUNET_OK != GNUNET_FUSE_download_file (pi, | ||
132 | 0, | ||
133 | GNUNET_FS_uri_chk_get_file_size (pi->uri))) | ||
134 | { | ||
135 | UNLINK (pi->tmpfile); | ||
136 | GNUNET_free (pi->tmpfile); | ||
137 | pi->tmpfile = NULL; | ||
138 | *eno = EIO; /* low level IO error */ | ||
139 | return GNUNET_SYSERR; | ||
140 | } | ||
141 | |||
142 | size = (size_t) GNUNET_FS_uri_chk_get_file_size (pi->uri); | ||
143 | fh = GNUNET_DISK_file_open (pi->tmpfile, | ||
144 | GNUNET_DISK_OPEN_READ, | ||
145 | GNUNET_DISK_PERM_NONE); | ||
146 | if (NULL == fh) | ||
147 | { | ||
148 | *eno = EIO; | ||
149 | return GNUNET_SYSERR; | ||
150 | } | ||
151 | data = GNUNET_DISK_file_map (fh, | ||
152 | &mh, | ||
153 | GNUNET_DISK_MAP_TYPE_READ, | ||
154 | size); | ||
155 | if (NULL == data) | ||
156 | { | ||
157 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
158 | return - ENOMEM; | ||
159 | } | ||
160 | *eno = 0; | ||
161 | if (GNUNET_OK != | ||
162 | GNUNET_FS_directory_list_contents (size, | ||
163 | data, 0LL, | ||
164 | &process_directory_entry, | ||
165 | pi)) | ||
166 | *eno = ENOTDIR; | ||
167 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_unmap (mh)); | ||
168 | GNUNET_DISK_file_close (fh); | ||
169 | if (0 != *eno) | ||
170 | return GNUNET_SYSERR; | ||
171 | return GNUNET_OK; | ||
172 | } | ||
69 | 173 | ||
70 | 174 | ||
71 | /** | 175 | /** |
72 | * Obtain an existing path info entry from the global map. | 176 | * Obtain an existing path info entry from the global map. |
73 | * | 177 | * |
74 | * @param path path the entry represents | 178 | * @param path path the entry represents |
179 | * @param eno where to store 'errno' on errors | ||
75 | * @return NULL if no such path entry exists | 180 | * @return NULL if no such path entry exists |
76 | */ | 181 | */ |
77 | struct GNUNET_FUSE_PathInfo * | 182 | struct GNUNET_FUSE_PathInfo * |
78 | GNUNET_FUSE_path_info_get (const char *path) | 183 | GNUNET_FUSE_path_info_get (const char *path, |
184 | int *eno) | ||
79 | { | 185 | { |
186 | size_t slen = strlen (path) + 1; | ||
187 | char buf[slen]; | ||
80 | struct GNUNET_FUSE_PathInfo *pi; | 188 | struct GNUNET_FUSE_PathInfo *pi; |
81 | GNUNET_HashCode path_hash; | 189 | struct GNUNET_FUSE_PathInfo *pos; |
82 | 190 | char *tok; | |
83 | GNUNET_CRYPTO_hash (path, strlen (path), &path_hash); | 191 | |
84 | GNUNET_mutex_lock (map_mutex); | 192 | memcpy (buf, path, slen); |
85 | pi = GNUNET_CONTAINER_multihashmap_get (map, &path_hash); | 193 | pi = root; |
86 | if (NULL != pi) | 194 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
87 | ++pi->rc; | 195 | "Looking up path `%s'\n", |
88 | GNUNET_mutex_unlock (map_mutex); | 196 | path); |
197 | GNUNET_mutex_lock (pi->lock); | ||
198 | for (tok = strtok (buf, "/"); NULL != tok; tok = strtok (NULL, "/")) | ||
199 | { | ||
200 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
201 | "Searching for token `%s'\n", | ||
202 | tok); | ||
203 | if (NULL == pi->tmpfile) | ||
204 | { | ||
205 | if (GNUNET_OK != GNUNET_FUSE_load_directory (pi, eno)) | ||
206 | { | ||
207 | GNUNET_mutex_unlock (pi->lock); | ||
208 | return NULL; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | pos = pi->child_head; | ||
213 | while ( (NULL != pos) && | ||
214 | (0 != strcmp (tok, | ||
215 | pos->filename)) ) | ||
216 | pos = pos->next; | ||
217 | if (NULL == pos) | ||
218 | { | ||
219 | GNUNET_mutex_unlock (pi->lock); | ||
220 | *eno = ENOENT; | ||
221 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
222 | "No file with name `%s' in directory `%s'\n", | ||
223 | tok, | ||
224 | pi->filename); | ||
225 | return NULL; | ||
226 | } | ||
227 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
228 | "Descending into directory `%s'\n", | ||
229 | tok); | ||
230 | GNUNET_mutex_lock (pos->lock); | ||
231 | GNUNET_mutex_unlock (pi->lock); | ||
232 | pi = pos; | ||
233 | } | ||
234 | ++pi->rc; | ||
235 | GNUNET_mutex_unlock (pi->lock); | ||
89 | return pi; | 236 | return pi; |
90 | } | 237 | } |
91 | 238 | ||
@@ -93,36 +240,37 @@ GNUNET_FUSE_path_info_get (const char *path) | |||
93 | /** | 240 | /** |
94 | * Create a new path info entry in the global map. | 241 | * Create a new path info entry in the global map. |
95 | * | 242 | * |
96 | * @param path path the entry represents | 243 | * @param parent parent directory (can be NULL) |
244 | * @param filename name of the file to create | ||
97 | * @param uri URI to use for the path | 245 | * @param uri URI to use for the path |
246 | * @param is_directory GNUNET_YES if this entry is for a directory | ||
98 | * @return existing path entry if one already exists, otherwise | 247 | * @return existing path entry if one already exists, otherwise |
99 | * new path entry with the desired URI | 248 | * new path entry with the desired URI; in both cases |
249 | * the reference counter has been incremented by 1 | ||
100 | */ | 250 | */ |
101 | struct GNUNET_FUSE_PathInfo * | 251 | struct GNUNET_FUSE_PathInfo * |
102 | GNUNET_FUSE_path_info_create (const char *path, | 252 | GNUNET_FUSE_path_info_create (struct GNUNET_FUSE_PathInfo *parent, |
253 | const char *filename, | ||
103 | const struct GNUNET_FS_Uri *uri, | 254 | const struct GNUNET_FS_Uri *uri, |
104 | int is_directory) | 255 | int is_directory) |
105 | { | 256 | { |
106 | struct GNUNET_FUSE_PathInfo *pi; | 257 | struct GNUNET_FUSE_PathInfo *pi; |
107 | GNUNET_HashCode path_hash; | 258 | size_t len; |
108 | 259 | ||
109 | GNUNET_CRYPTO_hash (path, strlen (path), &path_hash); | 260 | if (NULL != parent) |
110 | GNUNET_mutex_lock (map_mutex); | ||
111 | pi = GNUNET_CONTAINER_multihashmap_get (map, &path_hash); | ||
112 | if (NULL != pi) | ||
113 | { | 261 | { |
114 | GNUNET_mutex_unlock (map_mutex); | 262 | GNUNET_mutex_lock (parent->lock); |
115 | return pi; | ||
116 | } | 263 | } |
264 | |||
117 | pi = GNUNET_malloc (sizeof (struct GNUNET_FUSE_PathInfo)); | 265 | pi = GNUNET_malloc (sizeof (struct GNUNET_FUSE_PathInfo)); |
118 | pi->path = GNUNET_strdup (path); | 266 | pi->parent = parent; |
267 | pi->filename = GNUNET_strdup (filename); | ||
268 | len = strlen (pi->filename); | ||
269 | if ('/' == pi->filename[len - 1]) | ||
270 | pi->filename[len - 1] = '\0'; | ||
119 | pi->uri = GNUNET_FS_uri_dup (uri); | 271 | pi->uri = GNUNET_FS_uri_dup (uri); |
120 | pi->lock = GNUNET_mutex_create (GNUNET_YES); | 272 | pi->lock = GNUNET_mutex_create (GNUNET_YES); |
121 | pi->rc = 1; | 273 | pi->rc = 1; |
122 | GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (map, | ||
123 | &path_hash, | ||
124 | pi, | ||
125 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
126 | pi->stbuf.st_mode = (S_IRUSR | S_IRGRP | S_IROTH); /* read-only */ | 274 | pi->stbuf.st_mode = (S_IRUSR | S_IRGRP | S_IROTH); /* read-only */ |
127 | if (GNUNET_YES == is_directory) | 275 | if (GNUNET_YES == is_directory) |
128 | { | 276 | { |
@@ -133,7 +281,14 @@ GNUNET_FUSE_path_info_create (const char *path, | |||
133 | pi->stbuf.st_mode |= S_IFREG; /* regular file */ | 281 | pi->stbuf.st_mode |= S_IFREG; /* regular file */ |
134 | pi->stbuf.st_size = (off_t) GNUNET_FS_uri_chk_get_file_size (uri); | 282 | pi->stbuf.st_size = (off_t) GNUNET_FS_uri_chk_get_file_size (uri); |
135 | } | 283 | } |
136 | GNUNET_mutex_unlock (map_mutex); | 284 | |
285 | if (NULL != parent) | ||
286 | { | ||
287 | GNUNET_CONTAINER_DLL_insert_tail (parent->child_head, | ||
288 | parent->child_tail, | ||
289 | pi); | ||
290 | GNUNET_mutex_unlock (parent->lock); | ||
291 | } | ||
137 | return pi; | 292 | return pi; |
138 | } | 293 | } |
139 | 294 | ||
@@ -151,14 +306,14 @@ GNUNET_FUSE_path_info_done (struct GNUNET_FUSE_PathInfo *pi) | |||
151 | (void) GNUNET_FUSE_path_info_delete (pi); | 306 | (void) GNUNET_FUSE_path_info_delete (pi); |
152 | return; | 307 | return; |
153 | } | 308 | } |
154 | GNUNET_mutex_lock (map_mutex); | 309 | GNUNET_mutex_lock (pi->lock); |
155 | --pi->rc; | 310 | --pi->rc; |
156 | GNUNET_mutex_unlock (map_mutex); | 311 | GNUNET_mutex_unlock (pi->lock); |
157 | } | 312 | } |
158 | 313 | ||
159 | 314 | ||
160 | /** | 315 | /** |
161 | * Delete a path info entry from the global map (does not actually | 316 | * Delete a path info entry from the tree (does not actually |
162 | * remove anything from the file system). Also decrements the RC. | 317 | * remove anything from the file system). Also decrements the RC. |
163 | * | 318 | * |
164 | * @param pi entry to remove | 319 | * @param pi entry to remove |
@@ -166,19 +321,32 @@ GNUNET_FUSE_path_info_done (struct GNUNET_FUSE_PathInfo *pi) | |||
166 | */ | 321 | */ |
167 | int | 322 | int |
168 | GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi) | 323 | GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi) |
169 | { | 324 | { |
170 | GNUNET_HashCode path_hash; | 325 | struct GNUNET_FUSE_PathInfo *parent = pi->parent; |
171 | int ret; | ||
172 | int rc; | 326 | int rc; |
327 | int ret; | ||
173 | 328 | ||
174 | GNUNET_CRYPTO_hash (pi->path, strlen (pi->path), &path_hash); | 329 | if (NULL != parent) |
175 | GNUNET_mutex_lock (map_mutex); | 330 | { |
176 | ret = GNUNET_CONTAINER_multihashmap_remove (map, &path_hash, pi); | 331 | ret = 0; |
332 | GNUNET_mutex_lock (parent->lock); | ||
333 | GNUNET_mutex_lock (pi->lock); | ||
334 | GNUNET_CONTAINER_DLL_remove (parent->child_head, | ||
335 | parent->child_tail, | ||
336 | pi); | ||
337 | pi->parent = NULL; | ||
338 | GNUNET_mutex_unlock (parent->lock); | ||
339 | } | ||
340 | else | ||
341 | { | ||
342 | ret = - ENOENT; | ||
343 | GNUNET_mutex_lock (pi->lock); | ||
344 | } | ||
177 | rc = --pi->rc; | 345 | rc = --pi->rc; |
178 | GNUNET_mutex_unlock (map_mutex); | ||
179 | if (0 != rc) | 346 | if (0 != rc) |
180 | { | 347 | { |
181 | pi->delete_later = GNUNET_YES; | 348 | pi->delete_later = GNUNET_YES; |
349 | GNUNET_mutex_unlock (pi->lock); | ||
182 | } | 350 | } |
183 | else | 351 | else |
184 | { | 352 | { |
@@ -187,33 +355,30 @@ GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi) | |||
187 | GNUNET_break (0 == UNLINK (pi->tmpfile)); | 355 | GNUNET_break (0 == UNLINK (pi->tmpfile)); |
188 | GNUNET_free (pi->tmpfile); | 356 | GNUNET_free (pi->tmpfile); |
189 | } | 357 | } |
190 | GNUNET_free (pi->path); | 358 | GNUNET_free (pi->filename); |
191 | GNUNET_FS_uri_destroy (pi->uri); | 359 | GNUNET_FS_uri_destroy (pi->uri); |
360 | GNUNET_mutex_unlock (pi->lock); | ||
192 | GNUNET_mutex_destroy (pi->lock); | 361 | GNUNET_mutex_destroy (pi->lock); |
193 | GNUNET_free (pi); | 362 | GNUNET_free (pi); |
194 | } | 363 | } |
195 | if (GNUNET_YES == ret) | 364 | return ret; |
196 | return 0; | ||
197 | return - ENOENT; | ||
198 | } | 365 | } |
199 | 366 | ||
200 | 367 | ||
201 | /** | 368 | /** |
202 | * Called on each entry in our global 'map' to clean it up. | 369 | * Called on each node in the path info tree to clean it up. |
203 | * | 370 | * |
204 | * @param cls closure, NULL | 371 | * @param pi path info to clean up |
205 | * @param key current key code | ||
206 | * @param value value in the hash map, a 'struct GNUNET_FUSE_PathInfo' | ||
207 | * @return GNUNET_YES (we should continue to iterate) | ||
208 | */ | 372 | */ |
209 | static int | 373 | static void |
210 | cleanup_path_info (void *cls, const GNUNET_HashCode * key, void *value) | 374 | cleanup_path_info (struct GNUNET_FUSE_PathInfo *pi) |
211 | { | 375 | { |
212 | struct GNUNET_FUSE_PathInfo *pi = value; | 376 | struct GNUNET_FUSE_PathInfo *pos; |
213 | 377 | ||
378 | while (NULL != (pos = pi->child_head)) | ||
379 | cleanup_path_info (pos); | ||
214 | ++pi->rc; | 380 | ++pi->rc; |
215 | (void) GNUNET_FUSE_path_info_delete (pi); | 381 | (void) GNUNET_FUSE_path_info_delete (pi); |
216 | return GNUNET_YES; | ||
217 | } | 382 | } |
218 | 383 | ||
219 | 384 | ||
@@ -249,8 +414,7 @@ run (void *cls, | |||
249 | int argc; | 414 | int argc; |
250 | struct GNUNET_FS_Uri *uri; | 415 | struct GNUNET_FS_Uri *uri; |
251 | char *emsg; | 416 | char *emsg; |
252 | const char *path = "/"; | 417 | int eno; |
253 | struct GNUNET_FUSE_PathInfo *pi; | ||
254 | 418 | ||
255 | cfg = c; | 419 | cfg = c; |
256 | ret = 0; | 420 | ret = 0; |
@@ -279,20 +443,31 @@ run (void *cls, | |||
279 | (GNUNET_YES != GNUNET_FS_uri_test_loc (uri)) ) | 443 | (GNUNET_YES != GNUNET_FS_uri_test_loc (uri)) ) |
280 | { | 444 | { |
281 | fprintf (stderr, | 445 | fprintf (stderr, |
282 | _("The given URI is not for a directory and can thus not be mounted\n"), | 446 | _("The given URI is not for a directory and can thus not be mounted\n")); |
283 | emsg); | ||
284 | ret = 4; | 447 | ret = 4; |
448 | GNUNET_FS_uri_destroy (uri); | ||
285 | return; | 449 | return; |
286 | } | 450 | } |
287 | 451 | ||
288 | map_mutex = GNUNET_mutex_create (GNUNET_NO); | 452 | root = GNUNET_FUSE_path_info_create (NULL, "/", uri, GNUNET_YES); |
289 | map = GNUNET_CONTAINER_multihashmap_create (1024); | 453 | if (GNUNET_OK != |
290 | pi = GNUNET_FUSE_path_info_create (path, uri, GNUNET_YES); | 454 | GNUNET_FUSE_load_directory (root, &eno)) |
455 | { | ||
456 | fprintf (stderr, | ||
457 | _("Failed to mount `%s': %s\n"), | ||
458 | source, | ||
459 | STRERROR (eno)); | ||
460 | ret = 5; | ||
461 | cleanup_path_info (root); | ||
462 | GNUNET_FS_uri_destroy (uri); | ||
463 | return; | ||
464 | } | ||
291 | 465 | ||
292 | if (GNUNET_YES == single_threaded) | 466 | if (GNUNET_YES == single_threaded) |
293 | argc = 5; | 467 | argc = 5; |
294 | else | 468 | else |
295 | argc = 2; | 469 | argc = 2; |
470 | |||
296 | { | 471 | { |
297 | char *a[argc + 1]; | 472 | char *a[argc + 1]; |
298 | a[0] = "gnunet-fuse"; | 473 | a[0] = "gnunet-fuse"; |
@@ -306,12 +481,7 @@ run (void *cls, | |||
306 | a[argc] = NULL; | 481 | a[argc] = NULL; |
307 | fuse_main (argc, a, &fops, NULL); | 482 | fuse_main (argc, a, &fops, NULL); |
308 | } | 483 | } |
309 | GNUNET_FUSE_path_info_done (pi); | 484 | cleanup_path_info (root); |
310 | GNUNET_CONTAINER_multihashmap_iterate (map, &cleanup_path_info, NULL); | ||
311 | GNUNET_CONTAINER_multihashmap_destroy (map); | ||
312 | map = NULL; | ||
313 | GNUNET_mutex_destroy (map_mutex); | ||
314 | map_mutex = NULL; | ||
315 | GNUNET_FS_uri_destroy (uri); | 485 | GNUNET_FS_uri_destroy (uri); |
316 | } | 486 | } |
317 | 487 | ||
diff --git a/src/fuse/gnunet-fuse.h b/src/fuse/gnunet-fuse.h index d8f567b..99137d8 100644 --- a/src/fuse/gnunet-fuse.h +++ b/src/fuse/gnunet-fuse.h | |||
@@ -52,8 +52,36 @@ extern const struct GNUNET_CONFIGURATION_Handle *cfg; | |||
52 | */ | 52 | */ |
53 | struct GNUNET_FUSE_PathInfo | 53 | struct GNUNET_FUSE_PathInfo |
54 | { | 54 | { |
55 | |||
56 | /** | ||
57 | * All files in a directory are kept in a DLL. | ||
58 | */ | ||
59 | struct GNUNET_FUSE_PathInfo *next; | ||
60 | |||
61 | /** | ||
62 | * All files in a directory are kept in a DLL. | ||
63 | */ | ||
64 | struct GNUNET_FUSE_PathInfo *prev; | ||
65 | |||
66 | /** | ||
67 | * Parent directory, NULL for the root. | ||
68 | */ | ||
69 | struct GNUNET_FUSE_PathInfo *parent; | ||
70 | |||
55 | /** | 71 | /** |
56 | * uri to corresponding path | 72 | * Head of linked list of entries in this directory |
73 | * (NULL if this is a file). | ||
74 | */ | ||
75 | struct GNUNET_FUSE_PathInfo *child_head; | ||
76 | |||
77 | /** | ||
78 | * Head of linked list of entries in this directory | ||
79 | * (NULL if this is a file). | ||
80 | */ | ||
81 | struct GNUNET_FUSE_PathInfo *child_tail; | ||
82 | |||
83 | /** | ||
84 | * URI of the file or directory. | ||
57 | */ | 85 | */ |
58 | struct GNUNET_FS_Uri *uri; | 86 | struct GNUNET_FS_Uri *uri; |
59 | 87 | ||
@@ -63,14 +91,15 @@ struct GNUNET_FUSE_PathInfo | |||
63 | struct GNUNET_CONTAINER_MetaData *meta; | 91 | struct GNUNET_CONTAINER_MetaData *meta; |
64 | 92 | ||
65 | /** | 93 | /** |
66 | * pathname | 94 | * Name of the file for this path (i.e. "home"). '/' for the root (all other |
95 | * filenames must not contain '/') | ||
67 | */ | 96 | */ |
68 | char* path; | 97 | char *filename; |
69 | 98 | ||
70 | /** | 99 | /** |
71 | * name of temporary file | 100 | * Name of temporary file, NULL if we never accessed this file or directory. |
72 | */ | 101 | */ |
73 | char* tmpfile; | 102 | char *tmpfile; |
74 | 103 | ||
75 | /** | 104 | /** |
76 | * file attributes | 105 | * file attributes |
@@ -78,12 +107,25 @@ struct GNUNET_FUSE_PathInfo | |||
78 | struct stat stbuf; | 107 | struct stat stbuf; |
79 | 108 | ||
80 | /** | 109 | /** |
81 | * Lock for exclusive access to this struct. | 110 | * Lock for exclusive access to this struct (i.e. for downloading blocks). |
111 | * Lock order: always lock parents before children. | ||
82 | */ | 112 | */ |
83 | struct GNUNET_Mutex *lock; | 113 | struct GNUNET_Mutex *lock; |
84 | 114 | ||
85 | /** | 115 | /** |
86 | * Reference counter. | 116 | * Beginning of a contiguous range of blocks of the file what we |
117 | * have downloaded already to 'tmpfile'. | ||
118 | */ | ||
119 | uint64_t download_start; | ||
120 | |||
121 | /** | ||
122 | * End of a contiguous range of blocks of the file what we | ||
123 | * have downloaded already to 'tmpfile'. | ||
124 | */ | ||
125 | uint64_t download_end; | ||
126 | |||
127 | /** | ||
128 | * Reference counter (used if the file is deleted while being opened, etc.) | ||
87 | */ | 129 | */ |
88 | unsigned int rc; | 130 | unsigned int rc; |
89 | 131 | ||
@@ -97,7 +139,8 @@ struct GNUNET_FUSE_PathInfo | |||
97 | /** | 139 | /** |
98 | * Create a new path info entry in the global map. | 140 | * Create a new path info entry in the global map. |
99 | * | 141 | * |
100 | * @param path path the entry represents | 142 | * @param parent parent directory (can be NULL) |
143 | * @param filename name of the file to create | ||
101 | * @param uri URI to use for the path | 144 | * @param uri URI to use for the path |
102 | * @param is_directory GNUNET_YES if this entry is for a directory | 145 | * @param is_directory GNUNET_YES if this entry is for a directory |
103 | * @return existing path entry if one already exists, otherwise | 146 | * @return existing path entry if one already exists, otherwise |
@@ -105,7 +148,8 @@ struct GNUNET_FUSE_PathInfo | |||
105 | * the reference counter has been incremented by 1 | 148 | * the reference counter has been incremented by 1 |
106 | */ | 149 | */ |
107 | struct GNUNET_FUSE_PathInfo * | 150 | struct GNUNET_FUSE_PathInfo * |
108 | GNUNET_FUSE_path_info_create (const char *path, | 151 | GNUNET_FUSE_path_info_create (struct GNUNET_FUSE_PathInfo *parent, |
152 | const char *filename, | ||
109 | const struct GNUNET_FS_Uri *uri, | 153 | const struct GNUNET_FS_Uri *uri, |
110 | int is_directory); | 154 | int is_directory); |
111 | 155 | ||
@@ -114,11 +158,13 @@ GNUNET_FUSE_path_info_create (const char *path, | |||
114 | * Obtain an existing path info entry from the global map. | 158 | * Obtain an existing path info entry from the global map. |
115 | * | 159 | * |
116 | * @param path path the entry represents | 160 | * @param path path the entry represents |
161 | * @param eno where to store 'errno' on errors | ||
117 | * @return NULL if no such path entry exists, otherwise | 162 | * @return NULL if no such path entry exists, otherwise |
118 | * an entry with incremented reference counter (!) | 163 | * an entry with incremented reference counter (!) |
119 | */ | 164 | */ |
120 | struct GNUNET_FUSE_PathInfo * | 165 | struct GNUNET_FUSE_PathInfo * |
121 | GNUNET_FUSE_path_info_get (const char *path); | 166 | GNUNET_FUSE_path_info_get (const char *path, |
167 | int *eno); | ||
122 | 168 | ||
123 | 169 | ||
124 | /** | 170 | /** |
@@ -141,9 +187,30 @@ int | |||
141 | GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi); | 187 | GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi); |
142 | 188 | ||
143 | 189 | ||
190 | /** | ||
191 | * Load and parse a directory. | ||
192 | * | ||
193 | * @param pi path to the directory | ||
194 | * @param eno where to store 'errno' on errors | ||
195 | * @return GNUNET_OK on success | ||
196 | */ | ||
197 | int | ||
198 | GNUNET_FUSE_load_directory (struct GNUNET_FUSE_PathInfo *pi, | ||
199 | int * eno); | ||
200 | |||
201 | |||
144 | /* FUSE function files */ | 202 | /* FUSE function files */ |
145 | int gn_getattr(const char *path, struct stat *stbuf); | 203 | int gn_getattr(const char *path, struct stat *stbuf); |
146 | 204 | ||
205 | int gn_open(const char *path, struct fuse_file_info *fi); | ||
206 | |||
207 | int gn_read(const char *path, char *buf, size_t size, off_t offset, | ||
208 | struct fuse_file_info *fi); | ||
209 | |||
210 | int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler, | ||
211 | off_t offset, struct fuse_file_info *fi); | ||
212 | |||
213 | |||
147 | int gn_mknod(const char *path, mode_t mode, dev_t rdev); | 214 | int gn_mknod(const char *path, mode_t mode, dev_t rdev); |
148 | 215 | ||
149 | int gn_mkdir(const char *path, mode_t mode); | 216 | int gn_mkdir(const char *path, mode_t mode); |
@@ -156,19 +223,11 @@ int gn_rename(const char *from, const char *to); | |||
156 | 223 | ||
157 | int gn_truncate(const char *path, off_t size); | 224 | int gn_truncate(const char *path, off_t size); |
158 | 225 | ||
159 | int gn_open(const char *path, struct fuse_file_info *fi); | ||
160 | |||
161 | int gn_read(const char *path, char *buf, size_t size, off_t offset, | ||
162 | struct fuse_file_info *fi); | ||
163 | |||
164 | int gn_write(const char *path, const char *buf, size_t size, off_t offset, | 226 | int gn_write(const char *path, const char *buf, size_t size, off_t offset, |
165 | struct fuse_file_info *fi); | 227 | struct fuse_file_info *fi); |
166 | 228 | ||
167 | int gn_release(const char *path, struct fuse_file_info *fi); | 229 | int gn_release(const char *path, struct fuse_file_info *fi); |
168 | 230 | ||
169 | int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler, | ||
170 | off_t offset, struct fuse_file_info *fi); | ||
171 | |||
172 | int gn_utimens(const char *path, const struct timespec ts[2]); | 231 | int gn_utimens(const char *path, const struct timespec ts[2]); |
173 | 232 | ||
174 | 233 | ||
diff --git a/src/fuse/open.c b/src/fuse/open.c index 3f31661..fe14c2e 100644 --- a/src/fuse/open.c +++ b/src/fuse/open.c | |||
@@ -54,10 +54,13 @@ int | |||
54 | gn_open (const char *path, struct fuse_file_info *fi) | 54 | gn_open (const char *path, struct fuse_file_info *fi) |
55 | { | 55 | { |
56 | struct GNUNET_FUSE_PathInfo *pi; | 56 | struct GNUNET_FUSE_PathInfo *pi; |
57 | int eno; | ||
57 | 58 | ||
58 | pi = GNUNET_FUSE_path_info_get (path); | 59 | pi = GNUNET_FUSE_path_info_get (path, &eno); |
59 | if (NULL == pi) | 60 | if (NULL == pi) |
60 | return - ENOENT; | 61 | return - eno; |
62 | /* NOTE: once we allow writes, we need to keep the RC | ||
63 | incremented until close... */ | ||
61 | GNUNET_FUSE_path_info_done (pi); | 64 | GNUNET_FUSE_path_info_done (pi); |
62 | if (O_RDONLY != (fi->flags & 3)) | 65 | if (O_RDONLY != (fi->flags & 3)) |
63 | return - EACCES; | 66 | return - EACCES; |
diff --git a/src/fuse/read.c b/src/fuse/read.c index d6774e0..de950a8 100644 --- a/src/fuse/read.c +++ b/src/fuse/read.c | |||
@@ -52,15 +52,22 @@ gn_read (const char *path, char *buf, size_t size, off_t offset, | |||
52 | struct GNUNET_FUSE_PathInfo *path_info; | 52 | struct GNUNET_FUSE_PathInfo *path_info; |
53 | uint64_t fsize; | 53 | uint64_t fsize; |
54 | struct GNUNET_DISK_FileHandle *fh; | 54 | struct GNUNET_DISK_FileHandle *fh; |
55 | int eno; | ||
55 | 56 | ||
56 | path_info = GNUNET_FUSE_path_info_get (path); | 57 | path_info = GNUNET_FUSE_path_info_get (path, &eno); |
57 | if (NULL == path_info) | 58 | if (NULL == path_info) |
59 | return - eno; | ||
60 | fsize = GNUNET_FS_uri_chk_get_file_size (path_info->uri); | ||
61 | if (offset > fsize) | ||
58 | { | 62 | { |
59 | /* FIXME: we might need to check which of the ancestors | 63 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
60 | exist and possibly download ancestral directories, | 64 | "No data available at offset %llu of file `%s'\n", |
61 | instead of directly giving up here... */ | 65 | (unsigned long long) offset, |
62 | return - ENOENT; | 66 | path); |
63 | } | 67 | return 0; |
68 | } | ||
69 | if (offset + size > fsize) | ||
70 | size = fsize - offset; | ||
64 | if (NULL == path_info->tmpfile) | 71 | if (NULL == path_info->tmpfile) |
65 | { | 72 | { |
66 | /* store to temporary file */ | 73 | /* store to temporary file */ |
@@ -76,8 +83,46 @@ gn_read (const char *path, char *buf, size_t size, off_t offset, | |||
76 | return - EIO; /* low level IO error */ | 83 | return - EIO; /* low level IO error */ |
77 | } | 84 | } |
78 | } | 85 | } |
86 | else | ||
87 | { | ||
88 | if ( (offset < path_info->download_start) || | ||
89 | (size + offset > path_info->download_end) ) | ||
90 | { | ||
91 | /* need to download some more... */ | ||
92 | if (GNUNET_OK != GNUNET_FUSE_download_file (path_info, | ||
93 | offset, | ||
94 | size)) | ||
95 | { | ||
96 | UNLINK (path_info->tmpfile); | ||
97 | GNUNET_free (path_info->tmpfile); | ||
98 | path_info->tmpfile = NULL; | ||
99 | GNUNET_FUSE_path_info_done (path_info); | ||
100 | return - EIO; /* low level IO error */ | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | /* combine ranges */ | ||
105 | if (path_info->download_start == path_info->download_end) | ||
106 | { | ||
107 | /* first range */ | ||
108 | path_info->download_start = offset; | ||
109 | path_info->download_end = offset + size; | ||
110 | } | ||
111 | else | ||
112 | { | ||
113 | /* only combine ranges if the resulting range would | ||
114 | be contiguous... */ | ||
115 | if ( (offset >= path_info->download_start) && | ||
116 | (offset <= path_info->download_end) && | ||
117 | (offset + size > path_info->download_end) ) | ||
118 | path_info->download_end = offset + size; | ||
119 | if ( (offset + size >= path_info->download_start) && | ||
120 | (offset + size <= path_info->download_end) && | ||
121 | (offset < path_info->download_start) ) | ||
122 | path_info->download_start = offset; | ||
123 | } | ||
124 | |||
79 | 125 | ||
80 | fsize = GNUNET_FS_uri_chk_get_file_size (path_info->uri); | ||
81 | fh = GNUNET_DISK_file_open (path_info->tmpfile, | 126 | fh = GNUNET_DISK_file_open (path_info->tmpfile, |
82 | GNUNET_DISK_OPEN_READ, | 127 | GNUNET_DISK_OPEN_READ, |
83 | GNUNET_DISK_PERM_NONE); | 128 | GNUNET_DISK_PERM_NONE); |
diff --git a/src/fuse/readdir.c b/src/fuse/readdir.c index 9cb914b..32bd10d 100644 --- a/src/fuse/readdir.c +++ b/src/fuse/readdir.c | |||
@@ -53,153 +53,29 @@ | |||
53 | #include "gnunet-fuse.h" | 53 | #include "gnunet-fuse.h" |
54 | #include "gfs_download.h" | 54 | #include "gfs_download.h" |
55 | 55 | ||
56 | /** | ||
57 | * Closure for 'process_directory_entry'. | ||
58 | */ | ||
59 | struct DepContext | ||
60 | { | ||
61 | /** | ||
62 | * Function to call on each entry. | ||
63 | */ | ||
64 | fuse_fill_dir_t filler; | ||
65 | |||
66 | /** | ||
67 | * 'buf' argument to give to filler. | ||
68 | */ | ||
69 | void *buf; | ||
70 | |||
71 | /** | ||
72 | * Basepath to add for the entries. | ||
73 | */ | ||
74 | const char *path; | ||
75 | }; | ||
76 | |||
77 | |||
78 | /** | ||
79 | * Function used to process entries in a directory. | ||
80 | * | ||
81 | * @param cls closure | ||
82 | * @param filename name of the file in the directory | ||
83 | * @param uri URI of the file | ||
84 | * @param metadata metadata for the file; metadata for | ||
85 | * the directory if everything else is NULL/zero | ||
86 | * @param length length of the available data for the file | ||
87 | * (of type size_t since data must certainly fit | ||
88 | * into memory; if files are larger than size_t | ||
89 | * permits, then they will certainly not be | ||
90 | * embedded with the directory itself). | ||
91 | * @param data data available for the file (length bytes) | ||
92 | */ | ||
93 | static void | ||
94 | process_directory_entry (void *cls, | ||
95 | const char *filename, | ||
96 | const struct GNUNET_FS_Uri * | ||
97 | uri, | ||
98 | const struct | ||
99 | GNUNET_CONTAINER_MetaData * | ||
100 | meta, size_t length, | ||
101 | const void *data) | ||
102 | { | ||
103 | struct DepContext *dc = cls; | ||
104 | struct GNUNET_FUSE_PathInfo *pi; | ||
105 | char *path; | ||
106 | int is_directory; | ||
107 | |||
108 | if (NULL == filename) | ||
109 | return; /* info about the directory itself */ | ||
110 | GNUNET_asprintf (&path, | ||
111 | "%s%s", | ||
112 | dc->path, | ||
113 | filename); | ||
114 | is_directory = GNUNET_FS_meta_data_test_for_directory (meta); | ||
115 | if (GNUNET_SYSERR == is_directory) | ||
116 | is_directory = GNUNET_NO; /* if in doubt, say no */ | ||
117 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
118 | "Listing filename `%s' in directory `%s' (%s)\n", | ||
119 | filename, | ||
120 | dc->path, | ||
121 | path); | ||
122 | pi = GNUNET_FUSE_path_info_create (path, uri, is_directory); | ||
123 | dc->filler (dc->buf, | ||
124 | filename, | ||
125 | &pi->stbuf, | ||
126 | 0); | ||
127 | GNUNET_FUSE_path_info_done (pi); | ||
128 | } | ||
129 | |||
130 | 56 | ||
131 | int | 57 | int |
132 | gn_readdir (const char *path, void *buf, fuse_fill_dir_t filler, | 58 | gn_readdir (const char *path, void *buf, fuse_fill_dir_t filler, |
133 | off_t offset, struct fuse_file_info *fi) | 59 | off_t offset, struct fuse_file_info *fi) |
134 | { | 60 | { |
135 | struct GNUNET_FUSE_PathInfo *path_info; | 61 | struct GNUNET_FUSE_PathInfo *path_info; |
136 | struct DepContext dc; | 62 | struct GNUNET_FUSE_PathInfo *pos; |
137 | size_t size; | 63 | int eno; |
138 | int ret; | ||
139 | void *data; | ||
140 | struct GNUNET_DISK_MapHandle *mh; | ||
141 | struct GNUNET_DISK_FileHandle *fh; | ||
142 | 64 | ||
143 | path_info = GNUNET_FUSE_path_info_get (path); | 65 | path_info = GNUNET_FUSE_path_info_get (path, &eno); |
144 | if (NULL == path_info) | 66 | if (NULL == path_info) |
145 | { | 67 | return - eno; |
146 | /* FIXME: we might need to check which of the ancestors | 68 | if ( (NULL == path_info->tmpfile) && |
147 | exist and possibly download ancestral directories, | 69 | (GNUNET_OK != GNUNET_FUSE_load_directory (path_info, &eno)) ) |
148 | instead of directly giving up here... */ | 70 | return - eno; |
149 | return - ENOENT; | ||
150 | } | ||
151 | |||
152 | if (NULL == path_info->tmpfile) | ||
153 | { | ||
154 | /* store to temporary file */ | ||
155 | path_info->tmpfile = GNUNET_DISK_mktemp ("gnunet-fuse-tempfile"); | ||
156 | if (GNUNET_OK != GNUNET_FUSE_download_file (path_info, | ||
157 | 0, | ||
158 | GNUNET_FS_uri_chk_get_file_size (path_info->uri))) | ||
159 | { | ||
160 | UNLINK (path_info->tmpfile); | ||
161 | GNUNET_free (path_info->tmpfile); | ||
162 | path_info->tmpfile = NULL; | ||
163 | GNUNET_FUSE_path_info_done (path_info); | ||
164 | return - EIO; /* low level IO error */ | ||
165 | } | ||
166 | } | ||
167 | |||
168 | dc.filler = filler; | ||
169 | dc.path = path; | ||
170 | dc.buf = buf; | ||
171 | size = (size_t) GNUNET_FS_uri_chk_get_file_size (path_info->uri); | ||
172 | fh = GNUNET_DISK_file_open (path_info->tmpfile, | ||
173 | GNUNET_DISK_OPEN_READ, | ||
174 | GNUNET_DISK_PERM_NONE); | ||
175 | if (NULL == fh) | ||
176 | { | ||
177 | GNUNET_FUSE_path_info_done (path_info); | ||
178 | return -EBADF; | ||
179 | } | ||
180 | data = GNUNET_DISK_file_map (fh, | ||
181 | &mh, | ||
182 | GNUNET_DISK_MAP_TYPE_READ, | ||
183 | size); | ||
184 | if (NULL == data) | ||
185 | { | ||
186 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); | ||
187 | GNUNET_FUSE_path_info_done (path_info); | ||
188 | return -EBADF; | ||
189 | } | ||
190 | filler (buf, ".", NULL, 0); | 71 | filler (buf, ".", NULL, 0); |
191 | filler (buf, "..", NULL, 0); | 72 | filler (buf, "..", NULL, 0); |
192 | ret = 0; | 73 | for (pos = path_info->child_head; NULL != pos; pos = pos->next) |
193 | if (GNUNET_OK != | 74 | filler (buf, pos->filename, |
194 | GNUNET_FS_directory_list_contents (size, | 75 | &pos->stbuf, |
195 | data, 0LL, | 76 | 0); |
196 | &process_directory_entry, | ||
197 | &dc)) | ||
198 | ret = - EIO; | ||
199 | GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_unmap (mh)); | ||
200 | GNUNET_DISK_file_close (fh); | ||
201 | GNUNET_FUSE_path_info_done (path_info); | 77 | GNUNET_FUSE_path_info_done (path_info); |
202 | return ret; | 78 | return 0; |
203 | } | 79 | } |
204 | 80 | ||
205 | /* end of readdir.c */ | 81 | /* end of readdir.c */ |