Code

dfbbae840185b354552e7d72e2763d49ff9cb369
[fusedav.git] / src / filecache.c
1 /* $Id$ */
3 /***
4   Copyright (c) 2004-2006 Lennart Poettering
6   Permission is hereby granted, free of charge, to any person
7   obtaining a copy of this software and associated documentation files
8   (the "Software"), to deal in the Software without restriction,
9   including without limitation the rights to use, copy, modify, merge,
10   publish, distribute, sublicense, and/or sell copies of the Software,
11   and to permit persons to whom the Software is furnished to do so,
12   subject to the following conditions:
14   The above copyright notice and this permission notice shall be
15   included in all copies or substantial portions of the Software.
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   SOFTWARE.
25 ***/
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
31 #include <errno.h>
32 #include <string.h>
33 #include <limits.h>
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <assert.h>
38 #include <pthread.h>
39 #include <inttypes.h>
40 #include <limits.h>
42 #include <ne_props.h>
43 #include <ne_uri.h>
44 #include <ne_session.h>
45 #include <ne_utils.h>
46 #include <ne_socket.h>
47 #include <ne_auth.h>
48 #include <ne_dates.h>
49 #include <ne_basic.h>
51 #include "filecache.h"
52 #include "statcache.h"
53 #include "fusedav.h"
54 #include "session.h"
56 struct file_info {
57     char *filename;
58     int fd;
59     off_t server_length, length, present;
61     int readable;
62     int writable;
64     int modified;
66     int ref, dead;
68     pthread_mutex_t mutex;
70     /* This field is locked by files_mutex, not by file_info->mutex */
71     struct file_info *next;
72 };
74 static struct file_info *files = NULL;
75 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
77 static int file_cache_sync_unlocked(struct file_info *fi);
79 void* file_cache_get(const char *path) {
80     struct file_info *f, *r = NULL;
82     pthread_mutex_lock(&files_mutex);
84     for (f = files; f; f = f->next) {
86         pthread_mutex_lock(&f->mutex);
87         if (!f->dead && f->filename && !strcmp(path, f->filename)) {
88             f->ref++;
89             r = f;
90         }
91         pthread_mutex_unlock(&f->mutex);
93         if (r)
94             break;
95     }
97     pthread_mutex_unlock(&files_mutex);
98     return f;
99 }
101 static void file_cache_free_unlocked(struct file_info *fi) {
102     assert(fi && fi->dead && fi->ref == 0);
104     free(fi->filename);
106     if (fi->fd >= 0)
107         close(fi->fd);
109     pthread_mutex_destroy(&fi->mutex);
110     free(fi);
113 void file_cache_unref(void *f) {
114     struct file_info *fi = f;
115     assert(fi);
117     pthread_mutex_lock(&fi->mutex);
119     assert(fi->ref >= 1);
120     fi->ref--;
122     if (!fi->ref && fi->dead) {
123         file_cache_sync_unlocked(fi);
124         file_cache_free_unlocked(fi);
125     }
127     pthread_mutex_unlock(&fi->mutex);
130 static void file_cache_unlink(struct file_info *fi) {
131     struct file_info *s, *prev;
132     assert(fi);
134     pthread_mutex_lock(&files_mutex);
136     for (s = files, prev = NULL; s; s = s->next) {
137         if (s == fi) {
138             if (prev)
139                 prev->next = s->next;
140             else
141                 files = s->next;
143             break;
144         }
146         prev = s;
147     }
149     pthread_mutex_unlock(&files_mutex);
152 int file_cache_close(void *f) {
153     struct file_info *fi = f;
154     int r = 0;
155     assert(fi);
157     file_cache_unlink(f);
159     pthread_mutex_lock(&fi->mutex);
160     fi->dead = 1;
161     pthread_mutex_unlock(&fi->mutex);
163     return r;
166 void* file_cache_open(const char *path, int flags) {
167     struct file_info *fi = NULL;
168     char tempfile[PATH_MAX];
169     const char *length = NULL;
170     ne_request *req = NULL;
171     ne_session *session;
173     if (!(session = session_get(1))) {
174         errno = EIO;
175         goto fail;
176     }
178     if ((fi = file_cache_get(path))) {
179         if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
180         if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
181         return fi;
182     }
184     fi = malloc(sizeof(struct file_info));
185     memset(fi, 0, sizeof(struct file_info));
186     fi->fd = -1;
188     fi->filename = strdup(path);
190     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
191     if ((fi->fd = mkstemp(tempfile)) < 0)
192         goto fail;
193     unlink(tempfile);
195     req = ne_request_create(session, "HEAD", path);
196     assert(req);
198     if (ne_request_dispatch(req) != NE_OK) {
199         fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
200         errno = ENOENT;
201         goto fail;
202     }
204     if (!(length = ne_get_response_header(req, "Content-Length")))
205         /* dirty hack, since Apache doesn't send the file size if the file is empty */
206         fi->server_length = fi->length = 0;
207     else
208         fi->server_length = fi->length = atoi(length);
210     ne_request_destroy(req);
212     if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
213     if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
215     pthread_mutex_init(&fi->mutex, NULL);
217     pthread_mutex_lock(&files_mutex);
218     fi->next = files;
219     files = fi;
220     pthread_mutex_unlock(&files_mutex);
222     fi->ref = 1;
224     return fi;
226 fail:
228     if (req)
229         ne_request_destroy(req);
231     if (fi) {
232         if (fi->fd >= 0)
233             close(fi->fd);
234         free(fi->filename);
235         free(fi);
236     }
238     return NULL;
241 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
243     ne_content_range64 range;
244     ne_session *session;
246     assert(fi);
248     if (!(session = session_get(1))) {
249         errno = EIO;
250         return -1;
251     }
253     if (l > fi->server_length)
254         l = fi->server_length;
256     if (l <= fi->present)
257         return 0;
259     if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
260         return -1;
262     range.start = fi->present;
263     range.end = l-1;
264     range.total = 0;
266     if (ne_get_range64(session, fi->filename, &range, fi->fd) != NE_OK) {
267         fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
268         errno = ENOENT;
269         return -1;
270     }
272     fi->present = l;
273     return 0;
276 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
277     struct file_info *fi = f;
278     ssize_t r = -1;
280     assert(fi && buf && size);
282     pthread_mutex_lock(&fi->mutex);
284     if (load_up_to_unlocked(fi, offset+size) < 0)
285         goto finish;
287     if ((r = pread(fi->fd, buf, size, offset)) < 0)
288         goto finish;
290 finish:
292     pthread_mutex_unlock(&fi->mutex);
294     return r;
297 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
298     struct file_info *fi = f;
299     ssize_t r = -1;
301     assert (fi);
303     pthread_mutex_lock(&fi->mutex);
305     if (!fi->writable) {
306         errno = EBADF;
307         goto finish;
308     }
310     if (load_up_to_unlocked(fi, offset) < 0)
311         goto finish;
313     if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
314         goto finish;
316     if (offset+size > fi->present)
317         fi->present = offset+size;
319     if (offset+size > fi->length)
320         fi->length = offset+size;
322     fi->modified = 1;
324 finish:
325     pthread_mutex_unlock(&fi->mutex);
327     return r;
330 int file_cache_truncate(void *f, off_t s) {
331     struct file_info *fi = f;
332     int r;
334     assert(fi);
336     pthread_mutex_lock(&fi->mutex);
338     fi->length = s;
339     r = ftruncate(fi->fd, fi->length);
341     pthread_mutex_unlock(&fi->mutex);
343     return r;
346 int file_cache_sync_unlocked(struct file_info *fi) {
347     int r = -1;
348     ne_session *session;
350     assert(fi);
352     if (!fi->writable) {
353         errno = EBADF;
354         goto finish;
355     }
357     if (!fi->modified) {
358         r = 0;
359         goto finish;
360     }
362     if (load_up_to_unlocked(fi, (off_t) -1) < 0)
363         goto finish;
365     if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
366         goto finish;
368     if (!(session = session_get(1))) {
369         errno = EIO;
370         goto finish;
371     }
373     if (ne_put(session, fi->filename, fi->fd)) {
374         fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
375         errno = ENOENT;
376         goto finish;
377     }
379     stat_cache_invalidate(fi->filename);
380     dir_cache_invalidate_parent(fi->filename);
382     r = 0;
384 finish:
386     return r;
389 int file_cache_sync(void *f) {
390     struct file_info *fi = f;
391     int r = -1;
392     assert(fi);
394     pthread_mutex_lock(&fi->mutex);
395     r = file_cache_sync_unlocked(fi);
396     pthread_mutex_unlock(&fi->mutex);
398     return r;
401 int file_cache_close_all(void) {
402     int r = 0;
404     pthread_mutex_lock(&files_mutex);
406     while (files) {
407         struct file_info *fi = files;
409         pthread_mutex_lock(&fi->mutex);
410         fi->ref++;
411         pthread_mutex_unlock(&fi->mutex);
413         pthread_mutex_unlock(&files_mutex);
414         file_cache_close(fi);
415         file_cache_unref(fi);
416         pthread_mutex_lock(&files_mutex);
417     }
419     pthread_mutex_unlock(&files_mutex);
421     return r;
424 off_t file_cache_get_size(void *f) {
425     struct file_info *fi = f;
427     assert(fi);
429     return fi->length;