diff options
Diffstat (limited to 'src/fuse/gnunet-fuse.c')
-rw-r--r-- | src/fuse/gnunet-fuse.c | 302 |
1 files changed, 236 insertions, 66 deletions
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 | ||