2 * gstfs - a gstreamer filesystem
16 #define min(a,b) ((a)<(b)?(a):(b))
17 #define max(a,b) ((a)>(b)?(a):(b))
19 /* per-mount options and data structures */
20 struct gstfs_mount_info
22 pthread_mutex_t cache_mutex
; /* protects file_cache, cache_lru accesses */
23 GHashTable
*file_cache
; /* cache of transcoded audio */
24 GQueue
*cache_lru
; /* queue of items in LRU order */
25 int max_cache_entries
; /* max # of entries in the cache */
26 char *source_mount
; /* directory we are mirroring */
27 char *src_ext
; /* extension of files we transcode */
28 char *dest_ext
; /* extension of target files */
31 /* This stuff is stored into file_cache by filename */
32 struct gstfs_file_info
34 char *filename
; /* hash key */
35 char *source_filename
; /* filename in other mount */
36 pthread_mutex_t mutex
; /* protects this file info */
37 size_t len
; /* size of file */
38 size_t alloc_len
; /* allocated size of buf */
39 char *buf
; /* completely converted file */
40 GList
*list_node
; /* pointer for cache_lru */
44 static struct gstfs_mount_info mount_info
;
46 static char *get_source_path(const char *filename
);
49 * Create a new gstfs_file_info object using the specified destination
52 struct gstfs_file_info
*get_file_info(const char *filename
)
54 struct gstfs_file_info
*fi
;
56 fi
= calloc(1, sizeof(struct gstfs_file_info
));
57 fi
->filename
= g_strdup(filename
);
58 fi
->source_filename
= get_source_path(filename
);
59 pthread_mutex_init(&fi
->mutex
, NULL
);
64 * Release a previously allocated gstfs_file_info object
66 void put_file_info(struct gstfs_file_info
*fi
)
69 g_free(fi
->source_filename
);
74 * Given a filename with extension "search", return a possibly reallocated
75 * string with "replace" on the end.
77 char *replace_ext(char *filename
, char *search
, char *replace
)
79 char *ext
= strrchr(filename
, '.');
80 if (ext
&& strcmp(ext
+1, search
) == 0)
83 filename
= g_strconcat(filename
, replace
, NULL
);
89 * Return true if filename has extension dest_ext
91 int is_target_type(const char *filename
)
93 char *ext
= strrchr(filename
, '.');
94 return (ext
&& strcmp(ext
+1, mount_info
.dest_ext
) == 0);
98 * Remove items from the file cache until below the maximum.
99 * This is relatively quick since we can find elements by looking at the
100 * head of the lru list and then do a single hash lookup to remove from
103 * Called with cache_mutex held.
105 static void expire_cache()
107 struct gstfs_file_info
*fi
;
109 while (g_queue_get_length(mount_info
.cache_lru
) >
110 mount_info
.max_cache_entries
)
112 fi
= (struct gstfs_file_info
*) g_queue_pop_head(mount_info
.cache_lru
);
113 g_hash_table_remove(mount_info
.file_cache
, fi
);
119 * If the path represents a file in the mirror filesystem, then
120 * look for it in the cache. If not, create a new file info.
122 * If it isn't a mirror file, return NULL.
124 static struct gstfs_file_info
*gstfs_lookup(const char *path
)
126 struct gstfs_file_info
*ret
;
128 if (!is_target_type(path
))
131 pthread_mutex_lock(&mount_info
.cache_mutex
);
132 ret
= g_hash_table_lookup(mount_info
.file_cache
, path
);
135 ret
= get_file_info(path
);
139 g_hash_table_replace(mount_info
.file_cache
, ret
->filename
, ret
);
142 // move to end of LRU
144 g_queue_unlink(mount_info
.cache_lru
, ret
->list_node
);
146 g_queue_push_tail(mount_info
.cache_lru
, ret
);
147 ret
->list_node
= mount_info
.cache_lru
->tail
;
152 pthread_mutex_unlock(&mount_info
.cache_mutex
);
157 * Given a filename from the fuse mount, return the corresponding filename
160 static char *get_source_path(const char *filename
)
164 source
= g_strdup_printf("%s%s", mount_info
.source_mount
, filename
);
165 source
= replace_ext(source
, mount_info
.dest_ext
, mount_info
.src_ext
);
169 int gstfs_statfs(const char *path
, struct statvfs
*buf
)
173 source_path
= get_source_path(path
);
174 if (statvfs(source_path
, buf
))
181 int gstfs_getattr(const char *path
, struct stat
*stbuf
)
185 struct gstfs_file_info
*converted
;
187 source_path
= get_source_path(path
);
189 if (stat(source_path
, stbuf
))
191 else if ((converted
= gstfs_lookup(path
)))
192 stbuf
->st_size
= converted
->len
;
198 static int read_cb(char *buf
, size_t size
, void *data
)
200 struct gstfs_file_info
*info
= (struct gstfs_file_info
*) data
;
202 size_t newsz
= info
->len
+ size
;
204 if (info
->alloc_len
< newsz
)
206 info
->alloc_len
= max(info
->alloc_len
* 2, newsz
);
207 info
->buf
= realloc(info
->buf
, info
->alloc_len
);
212 memcpy(&info
->buf
[info
->len
], buf
, size
);
217 int gstfs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
218 struct fuse_file_info
*fi
)
220 struct gstfs_file_info
*info
= gstfs_lookup(path
);
226 pthread_mutex_lock(&info
->mutex
);
229 transcode(info
->source_filename
, read_cb
, info
);
231 if (info
->len
<= offset
)
234 count
= min(info
->len
- offset
, size
);
236 memcpy(buf
, &info
->buf
[offset
], count
);
239 pthread_mutex_unlock(&info
->mutex
);
243 int gstfs_open(const char *path
, struct fuse_file_info
*fi
)
245 struct gstfs_file_info
*info
= gstfs_lookup(path
);
253 * copy all entries from source mount
255 int gstfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
256 off_t offset
, struct fuse_file_info
*fi
)
258 struct dirent
*dirent
;
262 source_path
= get_source_path(path
);
263 dir
= opendir(source_path
);
268 while ((dirent
= readdir(dir
)))
270 char *s
= g_strdup(dirent
->d_name
);
271 s
= replace_ext(s
, mount_info
.src_ext
, mount_info
.dest_ext
);
272 filler(buf
, s
, NULL
, 0);
281 static struct fuse_operations gstfs_ops
= {
282 .readdir
= gstfs_readdir
,
283 .statfs
= gstfs_statfs
,
284 .getattr
= gstfs_getattr
,
289 int main(int argc
, char *argv
[])
291 pthread_mutex_init(&mount_info
.cache_mutex
, NULL
);
292 mount_info
.file_cache
= g_hash_table_new(g_str_hash
, g_str_equal
);
293 mount_info
.cache_lru
= g_queue_new();
294 mount_info
.max_cache_entries
= 50;
295 mount_info
.source_mount
= "ogg";
296 mount_info
.src_ext
= "ogg";
297 mount_info
.dest_ext
= "mp3";
299 gst_init(&argc
, &argv
);
300 return fuse_main(argc
, argv
, &gstfs_ops
, NULL
);