whitespace cleanup
[fusedav.git] / src / filecache.c
blobdfbbae840185b354552e7d72e2763d49ff9cb369
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;
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;
91 pthread_mutex_unlock(&f->mutex);
93 if (r)
94 break;
97 pthread_mutex_unlock(&files_mutex);
98 return f;
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);
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;
146 prev = s;
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;
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;
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;
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);
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;
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;
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;
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;
357 if (!fi->modified) {
358 r = 0;
359 goto finish;
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;
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;
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);
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;