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
44 #include <ne_session.h>
46 #include <ne_socket.h>
51 #include "filecache.h"
52 #include "statcache.h"
59 off_t server_length
, length
, present
;
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
)) {
91 pthread_mutex_unlock(&f
->mutex
);
97 pthread_mutex_unlock(&files_mutex
);
101 static void file_cache_free_unlocked(struct file_info
*fi
) {
102 assert(fi
&& fi
->dead
&& fi
->ref
== 0);
109 pthread_mutex_destroy(&fi
->mutex
);
113 void file_cache_unref(void *f
) {
114 struct file_info
*fi
= f
;
117 pthread_mutex_lock(&fi
->mutex
);
119 assert(fi
->ref
>= 1);
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
;
134 pthread_mutex_lock(&files_mutex
);
136 for (s
= files
, prev
= NULL
; s
; s
= s
->next
) {
139 prev
->next
= s
->next
;
149 pthread_mutex_unlock(&files_mutex
);
152 int file_cache_close(void *f
) {
153 struct file_info
*fi
= f
;
157 file_cache_unlink(f
);
159 pthread_mutex_lock(&fi
->mutex
);
161 pthread_mutex_unlock(&fi
->mutex
);
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
;
173 if (!(session
= session_get(1))) {
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;
184 fi
= malloc(sizeof(struct file_info
));
185 memset(fi
, 0, sizeof(struct file_info
));
188 fi
->filename
= strdup(path
);
190 snprintf(tempfile
, sizeof(tempfile
), "%s/fusedav-cache-XXXXXX", "/tmp");
191 if ((fi
->fd
= mkstemp(tempfile
)) < 0)
195 req
= ne_request_create(session
, "HEAD", path
);
198 if (ne_request_dispatch(req
) != NE_OK
) {
199 fprintf(stderr
, "HEAD failed: %s\n", ne_get_error(session
));
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;
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
);
220 pthread_mutex_unlock(&files_mutex
);
229 ne_request_destroy(req
);
241 static int load_up_to_unlocked(struct file_info
*fi
, off_t l
) {
243 ne_content_range64 range
;
248 if (!(session
= session_get(1))) {
253 if (l
> fi
->server_length
)
254 l
= fi
->server_length
;
256 if (l
<= fi
->present
)
259 if (lseek(fi
->fd
, fi
->present
, SEEK_SET
) != fi
->present
)
262 range
.start
= fi
->present
;
266 if (ne_get_range64(session
, fi
->filename
, &range
, fi
->fd
) != NE_OK
) {
267 fprintf(stderr
, "GET failed: %s\n", ne_get_error(session
));
276 int file_cache_read(void *f
, char *buf
, size_t size
, off_t offset
) {
277 struct file_info
*fi
= f
;
280 assert(fi
&& buf
&& size
);
282 pthread_mutex_lock(&fi
->mutex
);
284 if (load_up_to_unlocked(fi
, offset
+size
) < 0)
287 if ((r
= pread(fi
->fd
, buf
, size
, offset
)) < 0)
292 pthread_mutex_unlock(&fi
->mutex
);
297 int file_cache_write(void *f
, const char *buf
, size_t size
, off_t offset
) {
298 struct file_info
*fi
= f
;
303 pthread_mutex_lock(&fi
->mutex
);
310 if (load_up_to_unlocked(fi
, offset
) < 0)
313 if ((r
= pwrite(fi
->fd
, buf
, size
, offset
)) < 0)
316 if (offset
+size
> fi
->present
)
317 fi
->present
= offset
+size
;
319 if (offset
+size
> fi
->length
)
320 fi
->length
= offset
+size
;
325 pthread_mutex_unlock(&fi
->mutex
);
330 int file_cache_truncate(void *f
, off_t s
) {
331 struct file_info
*fi
= f
;
336 pthread_mutex_lock(&fi
->mutex
);
339 r
= ftruncate(fi
->fd
, fi
->length
);
341 pthread_mutex_unlock(&fi
->mutex
);
346 int file_cache_sync_unlocked(struct file_info
*fi
) {
362 if (load_up_to_unlocked(fi
, (off_t
) -1) < 0)
365 if (lseek(fi
->fd
, 0, SEEK_SET
) == (off_t
)-1)
368 if (!(session
= session_get(1))) {
373 if (ne_put(session
, fi
->filename
, fi
->fd
)) {
374 fprintf(stderr
, "PUT failed: %s\n", ne_get_error(session
));
379 stat_cache_invalidate(fi
->filename
);
380 dir_cache_invalidate_parent(fi
->filename
);
389 int file_cache_sync(void *f
) {
390 struct file_info
*fi
= f
;
394 pthread_mutex_lock(&fi
->mutex
);
395 r
= file_cache_sync_unlocked(fi
);
396 pthread_mutex_unlock(&fi
->mutex
);
401 int file_cache_close_all(void) {
404 pthread_mutex_lock(&files_mutex
);
407 struct file_info
*fi
= files
;
409 pthread_mutex_lock(&fi
->mutex
);
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
);
424 off_t
file_cache_get_size(void *f
) {
425 struct file_info
*fi
= f
;