Code

add experimental 64bit off_t workaround
[fusedav.git] / src / filecache.c
1 /* $Id$ */
3 /***
4   This file is part of fusedav.
6   fusedav is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10   
11   fusedav is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14   License for more details.
15   
16   You should have received a copy of the GNU General Public License
17   along with fusedav; if not, write to the Free Software Foundation,
18   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ***/
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <errno.h>
26 #include <string.h>
27 #include <limits.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <assert.h>
32 #include <pthread.h>
33 #include <inttypes.h>
34 #include <limits.h>
36 #include <ne_props.h>
37 #include <ne_uri.h>
38 #include <ne_session.h>
39 #include <ne_utils.h>
40 #include <ne_socket.h>
41 #include <ne_auth.h>
42 #include <ne_dates.h>
43 #include <ne_basic.h>
45 #include "filecache.h"
46 #include "statcache.h"
47 #include "fusedav.h"
48 #include "session.h"
50 struct file_info {
51     char *filename;
52     int fd;
53     off_t server_length, length, present;
54     
55     int readable;
56     int writable;
58     int modified;
60     int ref, dead;
62     pthread_mutex_t mutex;
64     /* This field is locked by files_mutex, not by file_info->mutex */
65     struct file_info *next;
66 };
68 static struct file_info *files = NULL;
69 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
71 static int file_cache_sync_unlocked(struct file_info *fi);
73 void* file_cache_get(const char *path) {
74     struct file_info *f, *r = NULL;
76     pthread_mutex_lock(&files_mutex);
77     
78     for (f = files; f; f = f->next) {
79         
80         pthread_mutex_lock(&f->mutex);
81         if (!f->dead && f->filename && !strcmp(path, f->filename)) {
82             f->ref++;
83             r = f;
84         }
85         pthread_mutex_unlock(&f->mutex);
87         if (r)
88             break;
89     }
90     
91     pthread_mutex_unlock(&files_mutex);
92     return f;
93 }
95 static void file_cache_free_unlocked(struct file_info *fi) {
96     assert(fi && fi->dead && fi->ref == 0);
98     free(fi->filename);
100     if (fi->fd >= 0)
101         close(fi->fd);
103     pthread_mutex_destroy(&fi->mutex);
104     free(fi);
107 void file_cache_unref(void *f) {
108     struct file_info *fi = f;
109     assert(fi);
111     pthread_mutex_lock(&fi->mutex);
113     assert(fi->ref >= 1);
114     fi->ref--;
116     if (!fi->ref && fi->dead) {
117         file_cache_sync_unlocked(fi);
118         file_cache_free_unlocked(fi);
119     }
121     pthread_mutex_unlock(&fi->mutex);
124 static void file_cache_unlink(struct file_info *fi) {
125     struct file_info *s, *prev;
126     assert(fi);
128     pthread_mutex_lock(&files_mutex);
129     
130     for (s = files, prev = NULL; s; s = s->next) {
131         if (s == fi) {
132             if (prev)
133                 prev->next = s->next;
134             else
135                 files = s->next;
137             break;
138         }
139         
140         prev = s;
141     }
142     
143     pthread_mutex_unlock(&files_mutex);
146 int file_cache_close(void *f) {
147     struct file_info *fi = f;
148     int r = 0;
149     assert(fi);
151     file_cache_unlink(f);
153     pthread_mutex_lock(&fi->mutex);
154     fi->dead = 1;
155     pthread_mutex_unlock(&fi->mutex);
157     return r;
160 void* file_cache_open(const char *path, int flags) {
161     struct file_info *fi = NULL;
162     char tempfile[PATH_MAX];
163     const char *length = NULL;
164     ne_request *req = NULL;
165     ne_session *session;
167     if (!(session = session_get(1))) {
168         errno = EIO;
169         goto fail;
170     }
172     if ((fi = file_cache_get(path))) {
173         if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
174         if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
175         return fi;
176     }
178     fi = malloc(sizeof(struct file_info));
179     memset(fi, 0, sizeof(struct file_info));
180     fi->fd = -1;
182     fi->filename = strdup(path);
184     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
185     if ((fi->fd = mkstemp(tempfile)) < 0)
186         goto fail;
187     unlink(tempfile);
189     req = ne_request_create(session, "HEAD", path);
190     assert(req);
192     if (ne_request_dispatch(req) != NE_OK) {
193         fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
194         errno = ENOENT;
195         goto fail;
196     }
198     if (!(length = ne_get_response_header(req, "Content-Length")))
199         /* dirty hack, since Apache doesn't send the file size if the file is empty */
200         fi->server_length = fi->length = 0; 
201     else
202         fi->server_length = fi->length = atoi(length);
204     ne_request_destroy(req);
205     
206     if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
207     if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
209     pthread_mutex_init(&fi->mutex, NULL);
210     
211     pthread_mutex_lock(&files_mutex);
212     fi->next = files;
213     files = fi;
214     pthread_mutex_unlock(&files_mutex);
216     fi->ref = 1;
217     
218     return fi;
220 fail:
222     if (req)
223         ne_request_destroy(req);
225     if (fi) {
226         if (fi->fd >= 0)
227             close(fi->fd);
228         free(fi->filename);
229         free(fi);
230     }
231         
232     return NULL;
235 #ifdef FIXNEON64
237 /* We assume that off_t is 64 bit, neon assumes it is 32 bit wide. we
238  * need to work around this somehow */
240 typedef struct {
241     uint32_t start, end, total;
242 } ne_content_range64;
244 #endif
246 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
248 #ifdef FIXNEON64
249     ne_content_range64 range;
250 #else
251     ne_content_range range;
252 #endif
253     ne_session *session;
255     assert(fi);
257     if (!(session = session_get(1))) {
258         errno = EIO;
259         return -1;
260     }
262     if (l > fi->server_length)
263         l = fi->server_length;
264     
265     if (l <= fi->present)
266         return 0;
268 #ifdef FIXNEON64
269     if (l > UINT_MAX) {
270         /* neon doesn't support 64bit ne_get_range right now */
271         errno = EIO;
272         return -1;
273     }
274 #endif
276     if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
277         return -1;
278     
279     range.start = fi->present;
280     range.end = l-1;
281     range.total = 0;
282     
283     if (ne_get_range(session, fi->filename, (ne_content_range*) &range, fi->fd) != NE_OK) {
284         fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
285         errno = ENOENT;
286         return -1;
287     }
289     fi->present = l;
290     return 0;
293 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
294     struct file_info *fi = f;
295     ssize_t r = -1;
296     
297     assert(fi && buf && size);
299     pthread_mutex_lock(&fi->mutex);
301     if (load_up_to_unlocked(fi, offset+size) < 0)
302         goto finish;
304     if ((r = pread(fi->fd, buf, size, offset)) < 0)
305         goto finish;
307 finish:
308     
309     pthread_mutex_unlock(&fi->mutex);
311     return r;
314 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
315     struct file_info *fi = f;
316     ssize_t r = -1;
318     assert (fi);
320     pthread_mutex_lock(&fi->mutex);
322     if (!fi->writable) {
323         errno = EBADF;
324         goto finish;
325     }
327     if (load_up_to_unlocked(fi, offset) < 0)
328         goto finish;
329         
330     if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
331         goto finish;
333     if (offset+size > fi->present)
334         fi->present = offset+size;
336     if (offset+size > fi->length)
337         fi->length = offset+size;
339     fi->modified = 1;
341 finish:
342     pthread_mutex_unlock(&fi->mutex);
343     
344     return r;
347 int file_cache_truncate(void *f, off_t s) {
348     struct file_info *fi = f;
349     int r;
351     assert(fi);
353     pthread_mutex_lock(&fi->mutex);
355     fi->length = s;
356     r = ftruncate(fi->fd, fi->length);
358     pthread_mutex_unlock(&fi->mutex);
360     return r;
363 int file_cache_sync_unlocked(struct file_info *fi) {
364     int r = -1;
365     ne_session *session;
367     assert(fi);
368     
369     if (!fi->writable) {
370         errno = EBADF;
371         goto finish;
372     }
374     if (!fi->modified) {
375         r = 0;
376         goto finish;
377     }
378     
379     if (load_up_to_unlocked(fi, (off_t) -1) < 0)
380         goto finish;
382     if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
383         goto finish;
385     if (!(session = session_get(1))) {
386         errno = EIO;
387         goto finish;
388     }
389     
390     if (ne_put(session, fi->filename, fi->fd)) {
391         fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
392         errno = ENOENT;
393         goto finish;
394     }
396     stat_cache_invalidate(fi->filename);
397     dir_cache_invalidate_parent(fi->filename);
399     r = 0;
401 finish:
402     
403     return r;
406 int file_cache_sync(void *f) {
407     struct file_info *fi = f;
408     int r = -1;
409     assert(fi);
411     pthread_mutex_lock(&fi->mutex);
412     r = file_cache_sync_unlocked(fi);
413     pthread_mutex_unlock(&fi->mutex);
414     
415     return r;
418 int file_cache_close_all(void) {
419     int r = 0;
421     pthread_mutex_lock(&files_mutex);
423     while (files) {
424         struct file_info *fi = files;
425         
426         pthread_mutex_lock(&fi->mutex);
427         fi->ref++;
428         pthread_mutex_unlock(&fi->mutex);
430         pthread_mutex_unlock(&files_mutex);
431         file_cache_close(fi);
432         file_cache_unref(fi);
433         pthread_mutex_lock(&files_mutex);
434     }
436     pthread_mutex_unlock(&files_mutex);
438     return r;
441 off_t file_cache_get_size(void *f) {
442     struct file_info *fi = f;
444     assert(fi);
446     return fi->length;