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.
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.
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;
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);
76 for (f = files; f; f = f->next) {
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 }
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);
103 }
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);
120 }
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);
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 }
138 prev = s;
139 }
141 pthread_mutex_unlock(&files_mutex);
142 }
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;
156 }
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);
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);
209 pthread_mutex_lock(&files_mutex);
210 fi->next = files;
211 files = fi;
212 pthread_mutex_unlock(&files_mutex);
214 fi->ref = 1;
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 }
230 return NULL;
231 }
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;
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;
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;
265 }
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;
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:
283 pthread_mutex_unlock(&fi->mutex);
285 return r;
286 }
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;
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);
318 return r;
319 }
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;
335 }
337 int file_cache_sync_unlocked(struct file_info *fi) {
338 int r = -1;
339 ne_session *session;
341 assert(fi);
343 if (!fi->writable) {
344 errno = EBADF;
345 goto finish;
346 }
348 if (!fi->modified) {
349 r = 0;
350 goto finish;
351 }
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 }
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:
377 return r;
378 }
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);
389 return r;
390 }
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;
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;
413 }
415 off_t file_cache_get_size(void *f) {
416 struct file_info *fi = f;
418 assert(fi);
420 return fi->length;
421 }