Code

autoconf stuff
[fusedav.git] / src / filecache.c
1 #define _XOPEN_SOURCE 500
3 #include <errno.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <fcntl.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <assert.h>
10 #include <pthread.h>
12 #include <ne_props.h>
13 #include <ne_uri.h>
14 #include <ne_session.h>
15 #include <ne_utils.h>
16 #include <ne_socket.h>
17 #include <ne_auth.h>
18 #include <ne_dates.h>
19 #include <ne_basic.h>
21 #include "filecache.h"
22 #include "fusedav.h"
23 #include "session.h"
24 #include "statcache.h"
26 struct file_info {
27     char *filename;
28     int fd;
29     off_t server_length, length, present;
30     
31     int readable;
32     int writable;
34     int modified;
36     int ref, dead;
38     pthread_mutex_t mutex;
40     /* This field is locked by files_mutex, not by file_info->mutex */
41     struct file_info *next;
42 };
44 static struct file_info *files = NULL;
45 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
47 int file_cache_sync_unlocked(struct file_info *fi);
49 void* file_cache_get(const char *path) {
50     struct file_info *f, *r = NULL;
52     pthread_mutex_lock(&files_mutex);
53     
54     for (f = files; f; f = f->next) {
55         
56         pthread_mutex_lock(&f->mutex);
57         if (!f->dead && f->filename && !strcmp(path, f->filename)) {
58             f->ref++;
59             r = f;
60         }
61         pthread_mutex_unlock(&f->mutex);
63         if (r)
64             break;
65     }
66     
67     pthread_mutex_unlock(&files_mutex);
68     return f;
69 }
71 static void file_cache_free_unlocked(struct file_info *fi) {
72     assert(fi && fi->dead && fi->ref == 0);
74     free(fi->filename);
76     if (fi->fd >= 0)
77         close(fi->fd);
79     pthread_mutex_destroy(&fi->mutex);
80     free(fi);
81 }
83 void file_cache_unref(void *f) {
84     struct file_info *fi = f;
85     assert(fi);
87     pthread_mutex_lock(&fi->mutex);
89     assert(fi->ref >= 1);
90     fi->ref--;
92     if (!fi->ref && fi->dead) {
93         file_cache_sync_unlocked(fi);
94         file_cache_free_unlocked(fi);
95     }
97     pthread_mutex_unlock(&fi->mutex);
98 }
100 static void file_cache_unlink(struct file_info *fi) {
101     struct file_info *s, *prev;
102     assert(fi);
104     pthread_mutex_lock(&files_mutex);
105     
106     for (s = files, prev = NULL; s; s = s->next) {
107         if (s == fi) {
108             if (prev)
109                 prev->next = s->next;
110             else
111                 files = s->next;
113             break;
114         }
115         
116         prev = s;
117     }
118     
119     pthread_mutex_unlock(&files_mutex);
122 int file_cache_close(void *f) {
123     struct file_info *fi = f;
124     int r = 0;
125     assert(fi);
127     file_cache_unlink(f);
129     pthread_mutex_lock(&fi->mutex);
130     fi->dead = 1;
131     pthread_mutex_unlock(&fi->mutex);
133     return r;
136 void* file_cache_open(const char *path, int flags) {
137     struct file_info *fi;
138     char tempfile[PATH_MAX];
139     char *length = NULL;
140     ne_request *req = NULL;
141     ne_session *session;
143     if ((fi = file_cache_get(path))) {
144         if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
145         if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
146         return fi;
147     }
149     if (!(session = session_get())) {
150         errno = -EIO;
151         return NULL;
152     }
154     fi = malloc(sizeof(struct file_info));
155     memset(fi, 0, sizeof(struct file_info));
156     fi->fd = -1;
158     fi->filename = strdup(path);
160     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
161     if ((fi->fd = mkstemp(tempfile)) < 0)
162         goto fail;
163     unlink(tempfile);
165     req = ne_request_create(session, "HEAD", path);
166     assert(req);
168     ne_add_response_header_handler(req, "Content-Length", ne_duplicate_header, &length);
169     
170     if (ne_request_dispatch(req) != NE_OK) {
171         fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
172         errno = ENOENT;
173         goto fail;
174     }
176     if (!length) {
177         fprintf(stderr, "HEAD did not return content length.\n");
178         errno = EPROTO;
179         goto fail;
180     }
182     fi->server_length = fi->length = atoi(length);
184     ne_request_destroy(req);
185     free(length);
186     
187     if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
188     if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
190     pthread_mutex_init(&fi->mutex, NULL);
191     
192     pthread_mutex_lock(&files_mutex);
193     fi->next = files;
194     files = fi;
195     pthread_mutex_unlock(&files_mutex);
197     fi->ref = 1;
198     
199     return fi;
201 fail:
203     if (req)
204         ne_request_destroy(req);
206     if (length)
207         free(length);
209     if (fi) {
210         if (fi->fd >= 0)
211             close(fi->fd);
212         free(fi->filename);
213         free(fi);
214     }
215         
216     return NULL;
219 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
220     ne_content_range range;
221     assert(fi);
222     ne_session *session;
224     if (!(session = session_get())) {
225         errno = EIO;
226         return -1;
227     }
229     if (l > fi->server_length)
230         l = fi->server_length;
231     
232     if (l <= fi->present)
233         return 0;
235     if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
236         return -1;
238     range.start = fi->present;
239     range.end = l-1;
240     
241     if (ne_get_range(session, fi->filename, &range, fi->fd)) {
242         fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
243         errno = ENOENT;
244         return -1;
245     }
247     fi->present = l;
248     return 0;
251 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
252     struct file_info *fi = f;
253     ssize_t r = -1;
254     
255     assert(fi && buf && size);
257     pthread_mutex_lock(&fi->mutex);
259     if (load_up_to_unlocked(fi, offset+size) < 0)
260         goto finish;
262     if ((r = pread(fi->fd, buf, size, offset)) < 0)
263         goto finish;
265 finish:
266     
267     pthread_mutex_unlock(&fi->mutex);
269     return r;
272 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
273     struct file_info *fi = f;
274     ssize_t r = -1;
276     assert (fi);
278     pthread_mutex_lock(&fi->mutex);
280     if (!fi->writable) {
281         errno = EBADF;
282         goto finish;
283     }
285     if (load_up_to_unlocked(fi, offset) < 0)
286         goto finish;
287         
288     if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
289         goto finish;
291     if (offset+size > fi->present)
292         fi->present = offset+size;
294     if (offset+size > fi->length)
295         fi->length = offset+size;
297     fi->modified = 1;
299     r = 0;
301 finish:
302     pthread_mutex_unlock(&fi->mutex);
303     
304     return r;
307 int file_cache_truncate(void *f, off_t s) {
308     struct file_info *fi = f;
309     assert(fi);
310     int r;
312     pthread_mutex_lock(&fi->mutex);
314     fi->length = s;
315     r = ftruncate(fi->fd, fi->length);
317     pthread_mutex_unlock(&fi->mutex);
319     return r;
322 int file_cache_sync_unlocked(struct file_info *fi) {
323     int r = -1;
324     ne_session *session;
325     assert(fi);
327     if (!(session = session_get())) {
328         errno = EIO;
329         goto finish;
330     }
332     if (!fi->writable) {
333         errno = EBADF;
334         goto finish;
335     }
337     if (!fi->modified) {
338         r = 0;
339         goto finish;
340     }
341     
342     if (load_up_to_unlocked(fi, (off_t) -1) < 0)
343         goto finish;
345     if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
346         goto finish;
348     
349     if (ne_put(session, fi->filename, fi->fd)) {
350         fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
351         errno = ENOENT;
352         goto finish;
353     }
355     stat_cache_invalidate(fi->filename);
356     dir_cache_invalidate_parent(fi->filename);
358     r = 0;
360 finish:
361     
362     return r;
365 int file_cache_sync(void *f) {
366     struct file_info *fi = f;
367     int r = -1;
368     assert(fi);
370     pthread_mutex_lock(&fi->mutex);
371     r = file_cache_sync_unlocked(fi);
372     pthread_mutex_unlock(&fi->mutex);
373     
374     return r;
377 int file_cache_close_all(void) {
378     int r = 0;
380     pthread_mutex_lock(&files_mutex);
382     while (files) {
383         struct file_info *fi = files;
384         
385         pthread_mutex_lock(&fi->mutex);
386         fi->ref++;
387         pthread_mutex_unlock(&fi->mutex);
389         pthread_mutex_unlock(&files_mutex);
390         file_cache_close(fi);
391         file_cache_unref(fi);
392         pthread_mutex_lock(&files_mutex);
393     }
395     pthread_mutex_unlock(&files_mutex);
397     return r;