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>
33 #include <inttypes.h>
34 #include <limits.h>
36 #include <ne_props.h>
37 #include <ne_uri.h>
38 #include <ne_session.h>
39 #include <ne_utils.h>
40 #include <ne_socket.h>
41 #include <ne_auth.h>
42 #include <ne_dates.h>
43 #include <ne_basic.h>
45 #include "filecache.h"
46 #include "statcache.h"
47 #include "fusedav.h"
48 #include "session.h"
50 struct file_info {
51 char *filename;
52 int fd;
53 off_t server_length, length, present;
55 int readable;
56 int writable;
58 int modified;
60 int ref, dead;
62 pthread_mutex_t mutex;
64 /* This field is locked by files_mutex, not by file_info->mutex */
65 struct file_info *next;
66 };
68 static struct file_info *files = NULL;
69 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
71 static int file_cache_sync_unlocked(struct file_info *fi);
73 void* file_cache_get(const char *path) {
74 struct file_info *f, *r = NULL;
76 pthread_mutex_lock(&files_mutex);
78 for (f = files; f; f = f->next) {
80 pthread_mutex_lock(&f->mutex);
81 if (!f->dead && f->filename && !strcmp(path, f->filename)) {
82 f->ref++;
83 r = f;
84 }
85 pthread_mutex_unlock(&f->mutex);
87 if (r)
88 break;
89 }
91 pthread_mutex_unlock(&files_mutex);
92 return f;
93 }
95 static void file_cache_free_unlocked(struct file_info *fi) {
96 assert(fi && fi->dead && fi->ref == 0);
98 free(fi->filename);
100 if (fi->fd >= 0)
101 close(fi->fd);
103 pthread_mutex_destroy(&fi->mutex);
104 free(fi);
105 }
107 void file_cache_unref(void *f) {
108 struct file_info *fi = f;
109 assert(fi);
111 pthread_mutex_lock(&fi->mutex);
113 assert(fi->ref >= 1);
114 fi->ref--;
116 if (!fi->ref && fi->dead) {
117 file_cache_sync_unlocked(fi);
118 file_cache_free_unlocked(fi);
119 }
121 pthread_mutex_unlock(&fi->mutex);
122 }
124 static void file_cache_unlink(struct file_info *fi) {
125 struct file_info *s, *prev;
126 assert(fi);
128 pthread_mutex_lock(&files_mutex);
130 for (s = files, prev = NULL; s; s = s->next) {
131 if (s == fi) {
132 if (prev)
133 prev->next = s->next;
134 else
135 files = s->next;
137 break;
138 }
140 prev = s;
141 }
143 pthread_mutex_unlock(&files_mutex);
144 }
146 int file_cache_close(void *f) {
147 struct file_info *fi = f;
148 int r = 0;
149 assert(fi);
151 file_cache_unlink(f);
153 pthread_mutex_lock(&fi->mutex);
154 fi->dead = 1;
155 pthread_mutex_unlock(&fi->mutex);
157 return r;
158 }
160 void* file_cache_open(const char *path, int flags) {
161 struct file_info *fi = NULL;
162 char tempfile[PATH_MAX];
163 const char *length = NULL;
164 ne_request *req = NULL;
165 ne_session *session;
167 if (!(session = session_get(1))) {
168 errno = EIO;
169 goto fail;
170 }
172 if ((fi = file_cache_get(path))) {
173 if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
174 if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
175 return fi;
176 }
178 fi = malloc(sizeof(struct file_info));
179 memset(fi, 0, sizeof(struct file_info));
180 fi->fd = -1;
182 fi->filename = strdup(path);
184 snprintf(tempfile, sizeof(tempfile), "%s/fusedav-cache-XXXXXX", "/tmp");
185 if ((fi->fd = mkstemp(tempfile)) < 0)
186 goto fail;
187 unlink(tempfile);
189 req = ne_request_create(session, "HEAD", path);
190 assert(req);
192 if (ne_request_dispatch(req) != NE_OK) {
193 fprintf(stderr, "HEAD failed: %s\n", ne_get_error(session));
194 errno = ENOENT;
195 goto fail;
196 }
198 if (!(length = ne_get_response_header(req, "Content-Length")))
199 /* dirty hack, since Apache doesn't send the file size if the file is empty */
200 fi->server_length = fi->length = 0;
201 else
202 fi->server_length = fi->length = atoi(length);
204 ne_request_destroy(req);
206 if (flags & O_RDONLY || flags & O_RDWR) fi->readable = 1;
207 if (flags & O_WRONLY || flags & O_RDWR) fi->writable = 1;
209 pthread_mutex_init(&fi->mutex, NULL);
211 pthread_mutex_lock(&files_mutex);
212 fi->next = files;
213 files = fi;
214 pthread_mutex_unlock(&files_mutex);
216 fi->ref = 1;
218 return fi;
220 fail:
222 if (req)
223 ne_request_destroy(req);
225 if (fi) {
226 if (fi->fd >= 0)
227 close(fi->fd);
228 free(fi->filename);
229 free(fi);
230 }
232 return NULL;
233 }
235 #ifdef FIXNEON64
237 /* We assume that off_t is 64 bit, neon assumes it is 32 bit wide. we
238 * need to work around this somehow */
240 typedef struct {
241 uint32_t start, end, total;
242 } ne_content_range64;
244 #endif
246 static int load_up_to_unlocked(struct file_info *fi, off_t l) {
248 #ifdef FIXNEON64
249 ne_content_range64 range;
250 #else
251 ne_content_range range;
252 #endif
253 ne_session *session;
255 assert(fi);
257 if (!(session = session_get(1))) {
258 errno = EIO;
259 return -1;
260 }
262 if (l > fi->server_length)
263 l = fi->server_length;
265 if (l <= fi->present)
266 return 0;
268 #ifdef FIXNEON64
269 if (l > UINT_MAX) {
270 /* neon doesn't support 64bit ne_get_range right now */
271 errno = EIO;
272 return -1;
273 }
274 #endif
276 if (lseek(fi->fd, fi->present, SEEK_SET) != fi->present)
277 return -1;
279 range.start = fi->present;
280 range.end = l-1;
281 range.total = 0;
283 if (ne_get_range(session, fi->filename, (ne_content_range*) &range, fi->fd) != NE_OK) {
284 fprintf(stderr, "GET failed: %s\n", ne_get_error(session));
285 errno = ENOENT;
286 return -1;
287 }
289 fi->present = l;
290 return 0;
291 }
293 int file_cache_read(void *f, char *buf, size_t size, off_t offset) {
294 struct file_info *fi = f;
295 ssize_t r = -1;
297 assert(fi && buf && size);
299 pthread_mutex_lock(&fi->mutex);
301 if (load_up_to_unlocked(fi, offset+size) < 0)
302 goto finish;
304 if ((r = pread(fi->fd, buf, size, offset)) < 0)
305 goto finish;
307 finish:
309 pthread_mutex_unlock(&fi->mutex);
311 return r;
312 }
314 int file_cache_write(void *f, const char *buf, size_t size, off_t offset) {
315 struct file_info *fi = f;
316 ssize_t r = -1;
318 assert (fi);
320 pthread_mutex_lock(&fi->mutex);
322 if (!fi->writable) {
323 errno = EBADF;
324 goto finish;
325 }
327 if (load_up_to_unlocked(fi, offset) < 0)
328 goto finish;
330 if ((r = pwrite(fi->fd, buf, size, offset)) < 0)
331 goto finish;
333 if (offset+size > fi->present)
334 fi->present = offset+size;
336 if (offset+size > fi->length)
337 fi->length = offset+size;
339 fi->modified = 1;
341 finish:
342 pthread_mutex_unlock(&fi->mutex);
344 return r;
345 }
347 int file_cache_truncate(void *f, off_t s) {
348 struct file_info *fi = f;
349 int r;
351 assert(fi);
353 pthread_mutex_lock(&fi->mutex);
355 fi->length = s;
356 r = ftruncate(fi->fd, fi->length);
358 pthread_mutex_unlock(&fi->mutex);
360 return r;
361 }
363 int file_cache_sync_unlocked(struct file_info *fi) {
364 int r = -1;
365 ne_session *session;
367 assert(fi);
369 if (!fi->writable) {
370 errno = EBADF;
371 goto finish;
372 }
374 if (!fi->modified) {
375 r = 0;
376 goto finish;
377 }
379 if (load_up_to_unlocked(fi, (off_t) -1) < 0)
380 goto finish;
382 if (lseek(fi->fd, 0, SEEK_SET) == (off_t)-1)
383 goto finish;
385 if (!(session = session_get(1))) {
386 errno = EIO;
387 goto finish;
388 }
390 if (ne_put(session, fi->filename, fi->fd)) {
391 fprintf(stderr, "PUT failed: %s\n", ne_get_error(session));
392 errno = ENOENT;
393 goto finish;
394 }
396 stat_cache_invalidate(fi->filename);
397 dir_cache_invalidate_parent(fi->filename);
399 r = 0;
401 finish:
403 return r;
404 }
406 int file_cache_sync(void *f) {
407 struct file_info *fi = f;
408 int r = -1;
409 assert(fi);
411 pthread_mutex_lock(&fi->mutex);
412 r = file_cache_sync_unlocked(fi);
413 pthread_mutex_unlock(&fi->mutex);
415 return r;
416 }
418 int file_cache_close_all(void) {
419 int r = 0;
421 pthread_mutex_lock(&files_mutex);
423 while (files) {
424 struct file_info *fi = files;
426 pthread_mutex_lock(&fi->mutex);
427 fi->ref++;
428 pthread_mutex_unlock(&fi->mutex);
430 pthread_mutex_unlock(&files_mutex);
431 file_cache_close(fi);
432 file_cache_unref(fi);
433 pthread_mutex_lock(&files_mutex);
434 }
436 pthread_mutex_unlock(&files_mutex);
438 return r;
439 }
441 off_t file_cache_get_size(void *f) {
442 struct file_info *fi = f;
444 assert(fi);
446 return fi->length;
447 }