1 #define FUSE_USE_VERSION 26
11 #include <curl/curl.h>
16 #include "urlencode.h"
20 #define INDEX_FILE "index.httpfs.html"
21 #define IN_ORDER_THRESHOLD (512*1024)
22 #define OUT_ORDER_THRESHOLD (1024*1024)
23 #define SEEK_FORWARD_THRESHOLD (64*1024)
27 * lepsi parsery (pro apache)
35 pthread_mutex_t conn_lock
= PTHREAD_MUTEX_INITIALIZER
;
37 static inline CURL
*get_conn(void)
39 pthread_mutex_lock(&conn_lock
);
43 static inline void put_conn(CURL
*conn
)
45 pthread_mutex_unlock(&conn_lock
);
48 static inline int is_a_index(char *s
)
56 if (bn
&& !strcmp(bn
+1, INDEX_FILE
)) {
63 void mkurl(struct grow
*url
, const char *path
, int slashend
)
66 grow_puts(url
, base_url
);
67 path_encode(url
, path
);
69 if (url
->s
[url
->ptr
-1] == '/') {
70 url
->s
[url
->ptr
-1] = 0;
80 static int chunk_init(struct handle
*h
)
82 h
->type
= HAND_RANDOM
;
87 static int chunk_read(struct handle
*h
, char *buf
, size_t size
, off_t offset
)
92 grow_init_static(&h
->data
, buf
, size
);
97 ret
= request(conn
, h
->url
.s
, 0, NULL
, &h
->data
, size
, offset
);
104 static void chunk_fini(struct handle
*h
)
110 /* actually, this will copy data several times:
111 * network stack -> curl buffer
112 * curl buffer -> grow buffer
113 * grow buffer -> fuse buffer
114 * fuse buffer -> kernel buffer
115 * kernel buffer -> user buffer
119 #define min(a,b) ((a)<(b)?(a):(b))
121 static int seq_init(struct handle
*h
, off_t offset
, size_t size
)
125 ret
= request_open_async(h
, offset
, size
);
129 grow_init(&h
->lover
);
134 static void seq_fini(struct handle
*h
)
136 if (!request_done(h
))
137 request_close_async(h
);
138 grow_free(&h
->lover
);
141 static int seq_read(struct handle
*h
, char *buf
, size_t size
, off_t offset
)
143 int done
= 0, ret
= 0;
144 struct grow
*lover
= &h
->lover
;
146 if (request_done(h
)) {
152 if (lover
->ptr
< size
) {
153 ret
= request_perf_async(h
, size
, &done
);
155 request_close_async(h
);
156 /* TODO sem prijde reconnect na timeout */
163 ret
= min(lover
->ptr
, size
);
165 memcpy(buf
, lover
->s
, ret
);
168 memmove(lover
->s
, lover
->s
+ret
, lover
->ptr
);
174 /* TODO: mergnout to s getattrem (hint: isindex a pak zmenit prava) */
175 static int determine_size(const char *path
, off_t
*size
)
182 memset(&stb
, 0, sizeof(struct stat
));
189 mkurl(&url
, path
, 0);
190 index
= is_a_index(url
.s
);
191 ret
= request(conn
, url
.s
, 1, &stb
, NULL
, 0, 0);
200 static int httpfs_getdir(const char *path
, fuse_cache_dirh_t h
, fuse_cache_dirfil_t filler
)
203 struct grow url
, page
= GROW_INIT
;
204 struct canon c
= { .h
= h
, .filler
= filler
};
212 memset(&stb
, 0, sizeof(stb
));
214 stb
.st_mode
= S_IFDIR
| 0555;
216 filler(h
, ".", &stb
);
217 filler(h
, "..", &stb
);
220 stb
.st_mode
= S_IFREG
| 0444;
221 filler(h
, INDEX_FILE
, &stb
);
224 mkurl(&url
, path
, 1);
226 ret
= request(conn
, url
.s
, 0, NULL
, &page
, 0, 0);
232 parse(page
.s
, canonize
, &c
);
241 static int httpfs_getattr(const char *path
, struct stat
*stb
)
247 memset(stb
, 0, sizeof(struct stat
));
249 if (!strcmp(path
, "/")) {
250 stb
->st_mode
= S_IFDIR
| 0555;
259 mkurl(&url
, path
, 0);
260 index
= is_a_index(url
.s
);
262 stb
->st_mode
= S_IFREG
| 0444;
263 ret
= request(conn
, url
.s
, 1, stb
, NULL
, 0, 0);
264 if (ret
== -EMLINK
) {
265 stb
->st_mode
= S_IFDIR
| 0555;
271 stb
->st_mode
= S_IFREG
| 0555;
279 static int httpfs_open(const char *path
, struct fuse_file_info
*fi
)
285 dbg("open of '%s' with mode '%x'\n", path
, fi
->flags
);
287 if ((fi
->flags
& 3) != O_RDONLY
)
290 /* dry run, determine size and/or support of partial content */
291 ret
= determine_size(path
, &size
);
294 dbg("page '%s': %s\n", path
, size
< 0 ? "no content len" : "content len");
297 h
= calloc(sizeof(*h
), 1);
298 mkurl(&h
->url
, path
, 0);
299 is_a_index(h
->url
.s
);
300 pthread_mutex_init(&h
->async_lock
, NULL
);
308 /* h->type |= HAND_HARD; */
310 fi
->fh
= (unsigned long) h
;
315 static int httpfs_read(const char *path
, char *buf
, size_t size
,
316 off_t offset
, struct fuse_file_info
*fi
)
320 h
= (struct handle
*) (uintptr_t) fi
->fh
;
322 pthread_mutex_lock(&h
->async_lock
);
323 if (!(h
->type
& HAND_HARD
) && offset
< h
->total
) {
324 if ((h
->type
& HAND_RANDOM
) && h
->in_order
> IN_ORDER_THRESHOLD
) {
325 dbg("READ: switching to SEQUENTIAL\n");
327 ret
= seq_init(h
, offset
, h
->total
- offset
);
329 dbg("READ: cannot switch to sequential (%d), forcing SOFT HARD\n", ret
);
330 h
->type
= HAND_RANDOM
| HAND_HARD
;
334 } else if (!(h
->type
& HAND_RANDOM
) && h
->out_order
> OUT_ORDER_THRESHOLD
) {
335 dbg("READ: switching to RANDOM\n");
338 h
->type
= HAND_RANDOM
;
342 dbg("read: %s %s ho:%lld io:%lld oo:%lld o:%lld s:%llu\n", (h
->type
&HAND_HARD
)?"hard":"soft", (h
->type
&HAND_RANDOM
)?"rand":"seq", h
->offset
, h
->in_order
, h
->out_order
, offset
, size
);
344 if (h
->offset
== offset
) {
345 if (h
->type
& HAND_RANDOM
) {
346 dbg("READ: reading next chunk RANDOM\n");
347 ret
= chunk_read(h
, buf
, size
, offset
);
349 dbg("READ: reading next chunk SEQUENTIAL\n");
350 ret
= seq_read(h
, buf
, size
, offset
);
361 if ((h
->type
& HAND_HARD
) && !(h
->type
& HAND_RANDOM
)) {
362 dbg("READ: reading chunk HARD SEQUENTIAL out of order\n");
364 if (h
->offset
< offset
&& offset
- h
->offset
< h
->total
) {
365 dbg("READ: small skip forwards: %d\n", offset
- h
->offset
);
366 seq_read(h
, NULL
, offset
- h
->offset
, h
->offset
);
367 ret
= seq_read(h
, buf
, size
, offset
);
369 h
->offset
= offset
+ ret
;
372 } else if (h
->type
& HAND_RANDOM
) {
373 dbg("READ: reading chunk SOFT RANDOM out of order\n");
374 ret
= chunk_read(h
, buf
, size
, offset
);
376 h
->offset
= offset
+ret
;
380 dbg("READ: reading chunk SOFT SEQUENTIAL out of order with chunk\n");
381 ret
= chunk_read(h
, buf
, size
, offset
);
383 h
->out_order
+= size
;
387 pthread_mutex_unlock(&h
->async_lock
);
391 static int httpfs_release(const char *path
, struct fuse_file_info
*fi
)
394 h
= (struct handle
*) (uintptr_t) fi
->fh
;
396 if (h
->type
& HAND_RANDOM
) {
406 static int httpfs_opendir(const char *path
, struct fuse_file_info
*fi
)
411 static int httpfs_releasedir(const char *path
, struct fuse_file_info
*fi
)
418 static struct fuse_cache_operations httpfs_oper
= {
420 .getattr
= httpfs_getattr
,
422 .release
= httpfs_release
,
423 .opendir
= httpfs_opendir
,
424 .releasedir
= httpfs_releasedir
,
427 .cache_getdir
= httpfs_getdir
,
432 static int httpfs_fuse_main(struct fuse_args
*args
)
434 #if FUSE_VERSION >= 26
435 return fuse_main(args
->argc
, args
->argv
, cache_init(&httpfs_oper
), NULL
);
437 return fuse_main(args
->argc
, args
->argv
, cache_init(&httpfs_oper
));
442 int main(int argc
, char *argv
[])
444 struct fuse_args args
= FUSE_ARGS_INIT(argc
-1, argv
+1);
450 if (base_url
[strlen(base_url
)-1] == '/')
451 base_url
[strlen(base_url
)-1] = 0;
453 conn
= curl_easy_init();
455 res
= cache_parse_options(&args
);
459 res
= httpfs_fuse_main(&args
);
460 fuse_opt_free_args(&args
);