Code

remove superfluous XOPEN define
[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>
34 #include <ne_props.h>
35 #include <ne_uri.h>
36 #include <ne_session.h>
37 #include <ne_utils.h>
38 #include <ne_socket.h>
39 #include <ne_auth.h>
40 #include <ne_dates.h>
41 #include <ne_basic.h>
43 #include "filecache.h"
44 #include "statcache.h"
45 #include "fusedav.h"
46 #include "session.h"
48 struct file_info {
49     char *filename;
50     int fd;
51     off_t server_length, length, present;
52     
53     int readable;
54     int writable;
56     int modified;
58     int ref, dead;
60     pthread_mutex_t mutex;
62     /* This field is locked by files_mutex, not by file_info->mutex */
63     struct file_info *next;
64 };
66 static struct file_info *files = NULL;
67 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static int file_cache_sync_unlocked(struct file_info *fi);
71 void* file_cache_get(const char *path) {
72     struct file_info *f, *r = NULL;
74     pthread_mutex_lock(&files_mutex);
75     
76     for (f = files; f; f = f->next) {
77         
78         pthread_mutex_lock(&f->mutex);
79         if (!f->dead && f->filename && !strcmp(path, f->filename)) {
80             f->ref++;
81             r = f;
82         }
83         pthread_mutex_unlock(&f->mutex);
85         if (r)
86             break;
87     }
88     
89     pthread_mutex_unlock(&files_mutex);
90     return f;
91 }
93 static void file_cache_free_unlocked(struct file_info *fi) {
94     assert(fi && fi->dead && fi->ref == 0);
96     free(fi->filename);
98     if (fi->fd >= 0)
99         close(fi->fd);
101     pthread_mutex_destroy(&fi->mutex);
102     free(fi);
105 void file_cache_unref(void *f) {
106     struct file_info *fi = f;
107     assert(fi);
109     pthread_mutex_lock(&fi->mutex);
111     assert(fi->ref >= 1);
112     fi->ref--;
114     if (!fi->ref && fi->dead) {
115         file_cache_sync_unlocked(fi);
116         file_cache_free_unlocked(fi);
117     }
119     pthread_mutex_unlock(&fi->mutex);
122 static void file_cache_unlink(struct file_info *fi) {
123     struct file_info *s, *prev;
124     assert(fi);
126     pthread_mutex_lock(&files_mutex);
127     
128     for (s = files, prev = NULL; s; s = s->next) {
129         if (s == fi) {
130             if (prev)
131                 prev->next = s->next;
132             else
133                 files = s->next;
135             break;
136         }
137         
138         prev = s;
139     }
140     
141     pthread_mutex_unlock(&files_mutex);
144 int file_cache_close(void *f) {
145     struct file_info *fi = f;
146     int r = 0;
147     assert(fi);
149     file_cache_unlink(f);
151     pthread_mutex_lock(&fi->mutex);
152     fi->dead = 1;
153     pthread_mutex_unlock(&fi->mutex);
155     return r;
158 void* file_cache_open(const char *path, int flags) {
159     struct file_info *fi;
160     char tempfile[PATH_MAX];
161     const char *length = NULL;
162     ne_request *req = NULL;
163     ne_session *session;
165     if (!(session = session_get(1))) {
166         errno = EIO;
167         goto fail;
168     }
170     if ((fi = file_cache_get(path))) {
171         if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
172         if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
173         return fi;
174     }
176     fi = malloc(sizeof(struct file_info));
177     memset(fi, 0, sizeof(struct file_info));
178     fi->fd = -1;
180     fi->filename = strdup(path);
182     snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
183     if ((fi->fd = mkstemp(tempfile)) < 0)
184         goto fail;
185     unlink(tempfile);
187     req = ne_request_create(session, "HEAD", path);
188     assert(req);
190     if (ne_request_dispatch(req) != NE_OK) {
191         fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
192         errno = ENOENT;
193         goto fail;
194     }
196     if (!(length = ne_get_response_header(req, "Content-Length")))
197         /* dirty hack, since Apache doesn't send the file size if the file is empty */
198         fi->server_length = fi->length = 0; 
199     else
200         fi->server_length = fi->length = atoi(length);
202     ne_request_destroy(req);
203     
204     if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
205     if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
207     pthread_mutex_init(&fi->mutex, NULL);
208     
209     pthread_mutex_lock(&files_mutex);
210     fi->next = files;
211     files = fi;
212     pthread_mutex_unlock(&files_mutex);
214     fi->ref = 1;
215     
216     return fi;
218 fail:
220     if (req)
221         ne_request_destroy(req);
223     if (fi) {
224         if (fi->fd >= 0)
225             close(fi->fd);
226         free(fi->filename);
227         free(fi);
228     }
229         
230     return NULL;
233 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
234     ne_content_range range;
235     ne_session *session;
237     assert(fi);
239     if (!(session = session_get(1))) {
240         errno = EIO;
241         return -1;
242     }
244     if (l > fi->server_length)
245         l = fi->server_length;
246     
247     if (l <= fi->present)
248         return 0;
250     if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
251         return -1;
253     range.start = fi->present;
254     range.end = l-1;
255     range.total = 0;
256     
257     if (ne_get_range(session, fi->filename, &range, fi->fd) != NE_OK) {
258         fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
259         errno = ENOENT;
260         return -1;
261     }
263     fi->present = l;
264     return 0;
267 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
268     struct file_info *fi = f;
269     ssize_t r = -1;
270     
271     assert(fi && buf && size);
273     pthread_mutex_lock(&fi->mutex);
275     if (load_up_to_unlocked(fi, offset+size) < 0)
276         goto finish;
278     if ((r = pread(fi->fd, buf, size, offset)) < 0)
279         goto finish;
281 finish:
282     
283     pthread_mutex_unlock(&fi->mutex);
285     return r;
288 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
289     struct file_info *fi = f;
290     ssize_t r = -1;
292     assert (fi);
294     pthread_mutex_lock(&fi->mutex);
296     if (!fi->writable) {
297         errno = EBADF;
298         goto finish;
299     }
301     if (load_up_to_unlocked(fi, offset) < 0)
302         goto finish;
303         
304     if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
305         goto finish;
307     if (offset+size > fi->present)
308         fi->present = offset+size;
310     if (offset+size > fi->length)
311         fi->length = offset+size;
313     fi->modified = 1;
315 finish:
316     pthread_mutex_unlock(&fi->mutex);
317     
318     return r;
321 int file_cache_truncate(void *f, off_t s) {
322     struct file_info *fi = f;
323     int r;
325     assert(fi);
327     pthread_mutex_lock(&fi->mutex);
329     fi->length = s;
330     r = ftruncate(fi->fd, fi->length);
332     pthread_mutex_unlock(&fi->mutex);
334     return r;
337 int file_cache_sync_unlocked(struct file_info *fi) {
338     int r = -1;
339     ne_session *session;
341     assert(fi);
342     
343     if (!fi->writable) {
344         errno = EBADF;
345         goto finish;
346     }
348     if (!fi->modified) {
349         r = 0;
350         goto finish;
351     }
352     
353     if (load_up_to_unlocked(fi, (off_t) -1) < 0)
354         goto finish;
356     if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
357         goto finish;
359     if (!(session = session_get(1))) {
360         errno = EIO;
361         goto finish;
362     }
363     
364     if (ne_put(session, fi->filename, fi->fd)) {
365         fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
366         errno = ENOENT;
367         goto finish;
368     }
370     stat_cache_invalidate(fi->filename);
371     dir_cache_invalidate_parent(fi->filename);
373     r = 0;
375 finish:
376     
377     return r;
380 int file_cache_sync(void *f) {
381     struct file_info *fi = f;
382     int r = -1;
383     assert(fi);
385     pthread_mutex_lock(&fi->mutex);
386     r = file_cache_sync_unlocked(fi);
387     pthread_mutex_unlock(&fi->mutex);
388     
389     return r;
392 int file_cache_close_all(void) {
393     int r = 0;
395     pthread_mutex_lock(&files_mutex);
397     while (files) {
398         struct file_info *fi = files;
399         
400         pthread_mutex_lock(&fi->mutex);
401         fi->ref++;
402         pthread_mutex_unlock(&fi->mutex);
404         pthread_mutex_unlock(&files_mutex);
405         file_cache_close(fi);
406         file_cache_unref(fi);
407         pthread_mutex_lock(&files_mutex);
408     }
410     pthread_mutex_unlock(&files_mutex);
412     return r;
415 off_t file_cache_get_size(void *f) {
416     struct file_info *fi = f;
418     assert(fi);
420     return fi->length;