aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-06-06 11:44:47 +0000
committerChristian Grothoff <christian@grothoff.org>2012-06-06 11:44:47 +0000
commit71eacc6a3ee0003df1485d15c44e9b7306105d48 (patch)
tree207716a9c0598d777c13e1fe053820ad96013c38
parent262e08b8d54f9dae5709509d766221f31e9ebaf9 (diff)
downloadgnunet-fuse-71eacc6a3ee0003df1485d15c44e9b7306105d48.tar.gz
gnunet-fuse-71eacc6a3ee0003df1485d15c44e9b7306105d48.zip
finishing read-only implementation of fuse for 0.9.3
-rw-r--r--src/fuse/getattr.c5
-rw-r--r--src/fuse/gnunet-fuse.c302
-rw-r--r--src/fuse/gnunet-fuse.h95
-rw-r--r--src/fuse/open.c7
-rw-r--r--src/fuse/read.c59
-rw-r--r--src/fuse/readdir.c148
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
43gn_getattr (const char *path, struct stat *stbuf) 43gn_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;
57static char *directory; 58static 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 */
63static struct GNUNET_CONTAINER_MultiHashMap *map; 63static 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 */
68static struct GNUNET_Mutex *map_mutex; 82static void
83process_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 */
117int
118GNUNET_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 */
77struct GNUNET_FUSE_PathInfo * 182struct GNUNET_FUSE_PathInfo *
78GNUNET_FUSE_path_info_get (const char *path) 183GNUNET_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 */
101struct GNUNET_FUSE_PathInfo * 251struct GNUNET_FUSE_PathInfo *
102GNUNET_FUSE_path_info_create (const char *path, 252GNUNET_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 */
167int 322int
168GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi) 323GNUNET_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 */
209static int 373static void
210cleanup_path_info (void *cls, const GNUNET_HashCode * key, void *value) 374cleanup_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 */
53struct GNUNET_FUSE_PathInfo 53struct 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 */
107struct GNUNET_FUSE_PathInfo * 150struct GNUNET_FUSE_PathInfo *
108GNUNET_FUSE_path_info_create (const char *path, 151GNUNET_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 */
120struct GNUNET_FUSE_PathInfo * 165struct GNUNET_FUSE_PathInfo *
121GNUNET_FUSE_path_info_get (const char *path); 166GNUNET_FUSE_path_info_get (const char *path,
167 int *eno);
122 168
123 169
124/** 170/**
@@ -141,9 +187,30 @@ int
141GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi); 187GNUNET_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 */
197int
198GNUNET_FUSE_load_directory (struct GNUNET_FUSE_PathInfo *pi,
199 int * eno);
200
201
144/* FUSE function files */ 202/* FUSE function files */
145int gn_getattr(const char *path, struct stat *stbuf); 203int gn_getattr(const char *path, struct stat *stbuf);
146 204
205int gn_open(const char *path, struct fuse_file_info *fi);
206
207int gn_read(const char *path, char *buf, size_t size, off_t offset,
208 struct fuse_file_info *fi);
209
210int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
211 off_t offset, struct fuse_file_info *fi);
212
213
147int gn_mknod(const char *path, mode_t mode, dev_t rdev); 214int gn_mknod(const char *path, mode_t mode, dev_t rdev);
148 215
149int gn_mkdir(const char *path, mode_t mode); 216int gn_mkdir(const char *path, mode_t mode);
@@ -156,19 +223,11 @@ int gn_rename(const char *from, const char *to);
156 223
157int gn_truncate(const char *path, off_t size); 224int gn_truncate(const char *path, off_t size);
158 225
159int gn_open(const char *path, struct fuse_file_info *fi);
160
161int gn_read(const char *path, char *buf, size_t size, off_t offset,
162 struct fuse_file_info *fi);
163
164int gn_write(const char *path, const char *buf, size_t size, off_t offset, 226int 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
167int gn_release(const char *path, struct fuse_file_info *fi); 229int gn_release(const char *path, struct fuse_file_info *fi);
168 230
169int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
170 off_t offset, struct fuse_file_info *fi);
171
172int gn_utimens(const char *path, const struct timespec ts[2]); 231int 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
54gn_open (const char *path, struct fuse_file_info *fi) 54gn_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 */
59struct 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 */
93static void
94process_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
131int 57int
132gn_readdir (const char *path, void *buf, fuse_fill_dir_t filler, 58gn_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 */