git: remove $Id$ svn cruft
[fusedav.git] / src / filecache.c
blob3b1e90728a4ccd8fc7b6317198e4102bd6113a39
1 /***
2 Copyright (c) 2004-2006 Lennart Poettering
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation files
6 (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software,
9 and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 ***/
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
29 #include <errno.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <assert.h>
36 #include <pthread.h>
37 #include <inttypes.h>
38 #include <limits.h>
40 #include <ne_props.h>
41 #include <ne_uri.h>
42 #include <ne_session.h>
43 #include <ne_utils.h>
44 #include <ne_socket.h>
45 #include <ne_auth.h>
46 #include <ne_dates.h>
47 #include <ne_basic.h>
49 #include "filecache.h"
50 #include "statcache.h"
51 #include "fusedav.h"
52 #include "session.h"
54 struct file_info {
55 char *filename;
56 int fd;
57 off_t server_length, length, present;
59 int readable;
60 int writable;
62 int modified;
64 int ref, dead;
66 pthread_mutex_t mutex;
68 /* This field is locked by files_mutex, not by file_info->mutex */
69 struct file_info *next;
72 static struct file_info *files = NULL;
73 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
75 static int file_cache_sync_unlocked(struct file_info *fi);
77 void* file_cache_get(const char *path) {
78 struct file_info *f, *r = NULL;
80 pthread_mutex_lock(&files_mutex);
82 for (f = files; f; f = f->next) {
84 pthread_mutex_lock(&f->mutex);
85 if (!f->dead && f->filename && !strcmp(path, f->filename)) {
86 f->ref++;
87 r = f;
89 pthread_mutex_unlock(&f->mutex);
91 if (r)
92 break;
95 pthread_mutex_unlock(&files_mutex);
96 return f;
99 static void file_cache_free_unlocked(struct file_info *fi) {
100 assert(fi && fi->dead && fi->ref == 0);
102 free(fi->filename);
104 if (fi->fd >= 0)
105 close(fi->fd);
107 pthread_mutex_destroy(&fi->mutex);
108 free(fi);
111 void file_cache_unref(void *f) {
112 struct file_info *fi = f;
113 assert(fi);
115 pthread_mutex_lock(&fi->mutex);
117 assert(fi->ref >= 1);
118 fi->ref--;
120 if (!fi->ref && fi->dead) {
121 file_cache_sync_unlocked(fi);
122 file_cache_free_unlocked(fi);
125 pthread_mutex_unlock(&fi->mutex);
128 static void file_cache_unlink(struct file_info *fi) {
129 struct file_info *s, *prev;
130 assert(fi);
132 pthread_mutex_lock(&files_mutex);
134 for (s = files, prev = NULL; s; s = s->next) {
135 if (s == fi) {
136 if (prev)
137 prev->next = s->next;
138 else
139 files = s->next;
141 break;
144 prev = s;
147 pthread_mutex_unlock(&files_mutex);
150 int file_cache_close(void *f) {
151 struct file_info *fi = f;
152 int r = 0;
153 assert(fi);
155 file_cache_unlink(f);
157 pthread_mutex_lock(&fi->mutex);
158 fi->dead = 1;
159 pthread_mutex_unlock(&fi->mutex);
161 return r;
164 void* file_cache_open(const char *path, int flags) {
165 struct file_info *fi = NULL;
166 char tempfile[PATH_MAX];
167 const char *length = NULL;
168 ne_request *req = NULL;
169 ne_session *session;
171 if (!(session = session_get(1))) {
172 errno = EIO;
173 goto fail;
176 if ((fi = file_cache_get(path))) {
177 if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
178 if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
179 return fi;
182 fi = malloc(sizeof(struct file_info));
183 memset(fi, 0, sizeof(struct file_info));
184 fi->fd = -1;
186 fi->filename = strdup(path);
188 snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
189 if ((fi->fd = mkstemp(tempfile)) < 0)
190 goto fail;
191 unlink(tempfile);
193 req = ne_request_create(session, "HEAD", path);
194 assert(req);
196 if (ne_request_dispatch(req) != NE_OK) {
197 fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
198 errno = ENOENT;
199 goto fail;
202 if (!(length = ne_get_response_header(req, "Content-Length")))
203 /* dirty hack, since Apache doesn't send the file size if the file is empty */
204 fi->server_length = fi->length = 0;
205 else
206 fi->server_length = fi->length = atoi(length);
208 ne_request_destroy(req);
210 if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
211 if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
213 pthread_mutex_init(&fi->mutex, NULL);
215 pthread_mutex_lock(&files_mutex);
216 fi->next = files;
217 files = fi;
218 pthread_mutex_unlock(&files_mutex);
220 fi->ref = 1;
222 return fi;
224 fail:
226 if (req)
227 ne_request_destroy(req);
229 if (fi) {
230 if (fi->fd >= 0)
231 close(fi->fd);
232 free(fi->filename);
233 free(fi);
236 return NULL;
239 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
241 ne_content_range64 range;
242 ne_session *session;
244 assert(fi);
246 if (!(session = session_get(1))) {
247 errno = EIO;
248 return -1;
251 if (l > fi->server_length)
252 l = fi->server_length;
254 if (l <= fi->present)
255 return 0;
257 if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
258 return -1;
260 range.start = fi->present;
261 range.end = l-1;
262 range.total = 0;
264 if (ne_get_range64(session, fi->filename, &range, fi->fd) != NE_OK) {
265 fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
266 errno = ENOENT;
267 return -1;
270 fi->present = l;
271 return 0;
274 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
275 struct file_info *fi = f;
276 ssize_t r = -1;
278 assert(fi && buf && size);
280 pthread_mutex_lock(&fi->mutex);
282 if (load_up_to_unlocked(fi, offset+size) < 0)
283 goto finish;
285 if ((r = pread(fi->fd, buf, size, offset)) < 0)
286 goto finish;
288 finish:
290 pthread_mutex_unlock(&fi->mutex);
292 return r;
295 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
296 struct file_info *fi = f;
297 ssize_t r = -1;
299 assert (fi);
301 pthread_mutex_lock(&fi->mutex);
303 if (!fi->writable) {
304 errno = EBADF;
305 goto finish;
308 if (load_up_to_unlocked(fi, offset) < 0)
309 goto finish;
311 if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
312 goto finish;
314 if (offset+size > fi->present)
315 fi->present = offset+size;
317 if (offset+size > fi->length)
318 fi->length = offset+size;
320 fi->modified = 1;
322 finish:
323 pthread_mutex_unlock(&fi->mutex);
325 return r;
328 int file_cache_truncate(void *f, off_t s) {
329 struct file_info *fi = f;
330 int r;
332 assert(fi);
334 pthread_mutex_lock(&fi->mutex);
336 fi->length = s;
337 r = ftruncate(fi->fd, fi->length);
339 pthread_mutex_unlock(&fi->mutex);
341 return r;
344 int file_cache_sync_unlocked(struct file_info *fi) {
345 int r = -1;
346 ne_session *session;
348 assert(fi);
350 if (!fi->writable) {
351 errno = EBADF;
352 goto finish;
355 if (!fi->modified) {
356 r = 0;
357 goto finish;
360 if (load_up_to_unlocked(fi, (off_t) -1) < 0)
361 goto finish;
363 if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
364 goto finish;
366 if (!(session = session_get(1))) {
367 errno = EIO;
368 goto finish;
371 if (ne_put(session, fi->filename, fi->fd)) {
372 fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
373 errno = ENOENT;
374 goto finish;
377 stat_cache_invalidate(fi->filename);
378 dir_cache_invalidate_parent(fi->filename);
380 r = 0;
382 finish:
384 return r;
387 int file_cache_sync(void *f) {
388 struct file_info *fi = f;
389 int r = -1;
390 assert(fi);
392 pthread_mutex_lock(&fi->mutex);
393 r = file_cache_sync_unlocked(fi);
394 pthread_mutex_unlock(&fi->mutex);
396 return r;
399 int file_cache_close_all(void) {
400 int r = 0;
402 pthread_mutex_lock(&files_mutex);
404 while (files) {
405 struct file_info *fi = files;
407 pthread_mutex_lock(&fi->mutex);
408 fi->ref++;
409 pthread_mutex_unlock(&fi->mutex);
411 pthread_mutex_unlock(&files_mutex);
412 file_cache_close(fi);
413 file_cache_unref(fi);
414 pthread_mutex_lock(&files_mutex);
417 pthread_mutex_unlock(&files_mutex);
419 return r;
422 off_t file_cache_get_size(void *f) {
423 struct file_info *fi = f;
425 assert(fi);
427 return fi->length;