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
42 #include <ne_session.h>
44 #include <ne_socket.h>
49 #include "filecache.h"
50 #include "statcache.h"
57 off_t server_length
, length
, present
;
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
)) {
89 pthread_mutex_unlock(&f
->mutex
);
95 pthread_mutex_unlock(&files_mutex
);
99 static void file_cache_free_unlocked(struct file_info
*fi
) {
100 assert(fi
&& fi
->dead
&& fi
->ref
== 0);
107 pthread_mutex_destroy(&fi
->mutex
);
111 void file_cache_unref(void *f
) {
112 struct file_info
*fi
= f
;
115 pthread_mutex_lock(&fi
->mutex
);
117 assert(fi
->ref
>= 1);
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
;
132 pthread_mutex_lock(&files_mutex
);
134 for (s
= files
, prev
= NULL
; s
; s
= s
->next
) {
137 prev
->next
= s
->next
;
147 pthread_mutex_unlock(&files_mutex
);
150 int file_cache_close(void *f
) {
151 struct file_info
*fi
= f
;
155 file_cache_unlink(f
);
157 pthread_mutex_lock(&fi
->mutex
);
159 pthread_mutex_unlock(&fi
->mutex
);
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
;
171 if (!(session
= session_get(1))) {
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;
182 fi
= malloc(sizeof(struct file_info
));
183 memset(fi
, 0, sizeof(struct file_info
));
186 fi
->filename
= strdup(path
);
188 snprintf(tempfile
, sizeof(tempfile
), "%s/fusedav-cache-XXXXXX", "/tmp");
189 if ((fi
->fd
= mkstemp(tempfile
)) < 0)
193 req
= ne_request_create(session
, "HEAD", path
);
196 if (ne_request_dispatch(req
) != NE_OK
) {
197 fprintf(stderr
, "HEAD failed: %s\n", ne_get_error(session
));
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;
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
);
218 pthread_mutex_unlock(&files_mutex
);
227 ne_request_destroy(req
);
239 static int load_up_to_unlocked(struct file_info
*fi
, off_t l
) {
241 ne_content_range64 range
;
246 if (!(session
= session_get(1))) {
251 if (l
> fi
->server_length
)
252 l
= fi
->server_length
;
254 if (l
<= fi
->present
)
257 if (lseek(fi
->fd
, fi
->present
, SEEK_SET
) != fi
->present
)
260 range
.start
= fi
->present
;
264 if (ne_get_range64(session
, fi
->filename
, &range
, fi
->fd
) != NE_OK
) {
265 fprintf(stderr
, "GET failed: %s\n", ne_get_error(session
));
274 int file_cache_read(void *f
, char *buf
, size_t size
, off_t offset
) {
275 struct file_info
*fi
= f
;
278 assert(fi
&& buf
&& size
);
280 pthread_mutex_lock(&fi
->mutex
);
282 if (load_up_to_unlocked(fi
, offset
+size
) < 0)
285 if ((r
= pread(fi
->fd
, buf
, size
, offset
)) < 0)
290 pthread_mutex_unlock(&fi
->mutex
);
295 int file_cache_write(void *f
, const char *buf
, size_t size
, off_t offset
) {
296 struct file_info
*fi
= f
;
301 pthread_mutex_lock(&fi
->mutex
);
308 if (load_up_to_unlocked(fi
, offset
) < 0)
311 if ((r
= pwrite(fi
->fd
, buf
, size
, offset
)) < 0)
314 if (offset
+size
> fi
->present
)
315 fi
->present
= offset
+size
;
317 if (offset
+size
> fi
->length
)
318 fi
->length
= offset
+size
;
323 pthread_mutex_unlock(&fi
->mutex
);
328 int file_cache_truncate(void *f
, off_t s
) {
329 struct file_info
*fi
= f
;
334 pthread_mutex_lock(&fi
->mutex
);
337 r
= ftruncate(fi
->fd
, fi
->length
);
339 pthread_mutex_unlock(&fi
->mutex
);
344 int file_cache_sync_unlocked(struct file_info
*fi
) {
360 if (load_up_to_unlocked(fi
, (off_t
) -1) < 0)
363 if (lseek(fi
->fd
, 0, SEEK_SET
) == (off_t
)-1)
366 if (!(session
= session_get(1))) {
371 if (ne_put(session
, fi
->filename
, fi
->fd
)) {
372 fprintf(stderr
, "PUT failed: %s\n", ne_get_error(session
));
377 stat_cache_invalidate(fi
->filename
);
378 dir_cache_invalidate_parent(fi
->filename
);
387 int file_cache_sync(void *f
) {
388 struct file_info
*fi
= f
;
392 pthread_mutex_lock(&fi
->mutex
);
393 r
= file_cache_sync_unlocked(fi
);
394 pthread_mutex_unlock(&fi
->mutex
);
399 int file_cache_close_all(void) {
402 pthread_mutex_lock(&files_mutex
);
405 struct file_info
*fi
= files
;
407 pthread_mutex_lock(&fi
->mutex
);
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
);
422 off_t
file_cache_get_size(void *f
) {
423 struct file_info
*fi
= f
;