summaryrefslogtreecommitdiff
path: root/firmware/common/fileobj_mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/fileobj_mgr.c')
-rw-r--r--firmware/common/fileobj_mgr.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c
new file mode 100644
index 0000000000..8e7831d36c
--- /dev/null
+++ b/firmware/common/fileobj_mgr.c
@@ -0,0 +1,396 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "system.h"
23#include "debug.h"
24#include "file.h"
25#include "dir.h"
26#include "disk_cache.h"
27#include "fileobj_mgr.h"
28#include "dircache_redirect.h"
29
30/**
31 * Manages file and directory streams on all volumes
32 *
33 * Intended for internal use by disk, file and directory code
34 */
35
36
37/* there will always be enough of these for all user handles, thus these
38 functions don't return failure codes */
39#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
40
41/* describes the file as an image on the storage medium */
42static struct fileobj_binding
43{
44 struct file_base_binding bind; /* base info list item (first!) */
45 uint16_t flags; /* F(D)(O)_* bits of this file/dir */
46 uint16_t writers; /* number of writer streams */
47 struct filestr_cache cache; /* write mode shared cache */
48 file_size_t size; /* size of this file */
49 struct ll_head list; /* open streams for this file/dir */
50} fobindings[MAX_FILEOBJS];
51static struct mutex stream_mutexes[MAX_FILEOBJS] SHAREDBSS_ATTR;
52static struct ll_head free_bindings;
53static struct ll_head busy_bindings[NUM_VOLUMES];
54
55#define BUSY_BINDINGS(volume) \
56 (&busy_bindings[IF_MV_VOL(volume)])
57
58#define BASEBINDING_LIST(bindp) \
59 (BUSY_BINDINGS(BASEBINDING_VOL(bindp)))
60
61#define FREE_BINDINGS() \
62 (&free_bindings)
63
64#define BINDING_FIRST(type, volume...) \
65 ((struct fileobj_binding *)type##_BINDINGS(volume)->head)
66
67#define BINDING_NEXT(fobp) \
68 ((struct fileobj_binding *)(fobp)->bind.node.next)
69
70#define FOR_EACH_BINDING(volume, fobp) \
71 for (struct fileobj_binding *fobp = BINDING_FIRST(BUSY, volume); \
72 fobp; fobp = BINDING_NEXT(fobp))
73
74#define STREAM_FIRST(fobp) \
75 ((struct filestr_base *)(fobp)->list.head)
76
77#define STREAM_NEXT(s) \
78 ((struct filestr_base *)(s)->node.next)
79
80#define STREAM_THIS(s) \
81 (s)
82
83#define FOR_EACH_STREAM(what, start, s) \
84 for (struct filestr_base *s = STREAM_##what(start); \
85 s; s = STREAM_NEXT(s))
86
87
88/* syncs information for the stream's old and new parent directory if any are
89 currently opened */
90static void fileobj_sync_parent(const struct file_base_info *infop[],
91 int count)
92{
93 FOR_EACH_BINDING(infop[0]->volume, fobp)
94 {
95 if ((fobp->flags & (FO_DIRECTORY|FO_REMOVED)) != FO_DIRECTORY)
96 continue; /* not directory or removed can't be parent of anything */
97
98 struct filestr_base *parentstrp = STREAM_FIRST(fobp);
99 struct fat_file *parentfilep = &parentstrp->infop->fatfile;
100
101 for (int i = 0; i < count; i++)
102 {
103 if (!fat_dir_is_parent(parentfilep, &infop[i]->fatfile))
104 continue;
105
106 /* discard scan/read caches' parent dir info */
107 FOR_EACH_STREAM(THIS, parentstrp, s)
108 filestr_discard_cache(s);
109 }
110 }
111}
112
113/* see if this file has open streams and return that fileobj_binding if so,
114 else grab a new one from the free list; returns true if this stream is
115 the only open one */
116static bool binding_assign(const struct file_base_info *srcinfop,
117 struct fileobj_binding **fobpp)
118{
119 FOR_EACH_BINDING(srcinfop->fatfile.volume, fobp)
120 {
121 if (fobp->flags & FO_REMOVED)
122 continue;
123
124 if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
125 {
126 /* already has open streams */
127 *fobpp = fobp;
128 return false;
129 }
130 }
131
132 /* not found - allocate anew */
133 *fobpp = BINDING_FIRST(FREE);
134 ll_remove_first(FREE_BINDINGS());
135 ll_init(&(*fobpp)->list);
136 return true;
137}
138
139/* mark descriptor as unused and return to the free list */
140static void binding_add_to_free_list(struct fileobj_binding *fobp)
141{
142 fobp->flags = 0;
143 ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
144}
145
146/** File and directory internal interface **/
147
148void file_binding_insert_last(struct file_base_binding *bindp)
149{
150 ll_insert_last(BASEBINDING_LIST(bindp), &bindp->node);
151}
152
153void file_binding_remove(struct file_base_binding *bindp)
154{
155 ll_remove(BASEBINDING_LIST(bindp), &bindp->node);
156}
157
158#ifdef HAVE_DIRCACHE
159void file_binding_insert_first(struct file_base_binding *bindp)
160{
161 ll_insert_first(BASEBINDING_LIST(bindp), &bindp->node);
162}
163
164void file_binding_remove_next(struct file_base_binding *prevp,
165 struct file_base_binding *bindp)
166{
167 ll_remove_next(BASEBINDING_LIST(bindp), &prevp->node);
168 (void)bindp;
169}
170#endif /* HAVE_DIRCACHE */
171
172/* opens the file object for a new stream and sets up the caches;
173 * the stream must already be opened at the FS driver level and *stream
174 * initialized.
175 *
176 * NOTE: switches stream->infop to the one kept in common for all streams of
177 * the same file, making a copy for only the first stream
178 */
179void fileobj_fileop_open(struct filestr_base *stream,
180 const struct file_base_info *srcinfop,
181 unsigned int callflags)
182{
183 struct fileobj_binding *fobp;
184 bool first = binding_assign(srcinfop, &fobp);
185
186 /* add stream to this file's list */
187 ll_insert_last(&fobp->list, &stream->node);
188
189 /* initiate the new stream into the enclave */
190 stream->flags = FDO_BUSY | (callflags & (FD_WRITE|FD_WRONLY|FD_APPEND));
191 stream->infop = &fobp->bind.info;
192 stream->fatstr.fatfilep = &fobp->bind.info.fatfile;
193 stream->bindp = &fobp->bind;
194 stream->mtx = &stream_mutexes[fobp - fobindings];
195
196 if (first)
197 {
198 /* first stream for file */
199 fobp->bind.info = *srcinfop;
200 fobp->flags = FDO_BUSY | FO_SINGLE |
201 (callflags & (FO_DIRECTORY|FO_TRUNC));
202 fobp->writers = 0;
203 fobp->size = 0;
204
205 if (callflags & FD_WRITE)
206 {
207 /* first one is a writer */
208 fobp->writers = 1;
209 file_cache_init(&fobp->cache);
210 filestr_assign_cache(stream, &fobp->cache);
211 }
212
213 fileobj_bind_file(&fobp->bind);
214 }
215 else
216 {
217 /* additional stream for file */
218 fobp->flags &= ~FO_SINGLE;
219 fobp->flags |= callflags & FO_TRUNC;
220
221 /* once a file/directory, always a file/directory; such a change
222 is a bug */
223 if ((callflags ^ fobp->flags) & FO_DIRECTORY)
224 {
225 DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
226 __func__, stream, callflags);
227 }
228
229 if (fobp->writers)
230 {
231 /* already writers present */
232 fobp->writers++;
233 filestr_assign_cache(stream, &fobp->cache);
234 }
235 else if (callflags & FD_WRITE)
236 {
237 /* first writer */
238 fobp->writers = 1;
239 file_cache_init(&fobp->cache);
240 FOR_EACH_STREAM(FIRST, fobp, s)
241 filestr_assign_cache(s, &fobp->cache);
242 }
243 /* else another reader */
244 }
245}
246
247/* close the stream and free associated resources */
248void fileobj_fileop_close(struct filestr_base *stream)
249{
250 switch (stream->flags)
251 {
252 case 0: /* not added to manager */
253 case FV_NONEXIST: /* forced-closed by unmounting */
254 filestr_base_destroy(stream);
255 return;
256 }
257
258 struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
259 unsigned int foflags = fobp->flags;
260
261 ll_remove(&fobp->list, &stream->node);
262
263 if ((foflags & FO_SINGLE) || fobp->writers == 0)
264 {
265 if (foflags & FO_SINGLE)
266 {
267 /* last stream for file; close everything */
268 fileobj_unbind_file(&fobp->bind);
269
270 if (fobp->writers)
271 file_cache_free(&fobp->cache);
272
273 binding_add_to_free_list(fobp);
274 }
275 }
276 else if ((stream->flags & FD_WRITE) && --fobp->writers == 0)
277 {
278 /* only readers remain; switch back to stream-local caching */
279 FOR_EACH_STREAM(FIRST, fobp, s)
280 filestr_copy_cache(s, &fobp->cache);
281
282 file_cache_free(&fobp->cache);
283 }
284
285 if (!(foflags & FO_SINGLE) && fobp->list.head == fobp->list.tail)
286 fobp->flags |= FO_SINGLE; /* only one open stream remaining */
287
288 filestr_base_destroy(stream);
289}
290
291/* informs manager that file has been created */
292void fileobj_fileop_create(struct filestr_base *stream,
293 const struct file_base_info *srcinfop,
294 unsigned int callflags)
295{
296 fileobj_fileop_open(stream, srcinfop, callflags);
297 fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
298}
299
300/* informs manager that file has been removed */
301void fileobj_fileop_remove(struct filestr_base *stream,
302 const struct file_base_info *oldinfop)
303{
304 ((struct fileobj_binding *)stream->bindp)->flags |= FO_REMOVED;
305 fileobj_sync_parent((const struct file_base_info *[]){ oldinfop }, 1);
306}
307
308/* informs manager that file has been renamed */
309void fileobj_fileop_rename(struct filestr_base *stream,
310 const struct file_base_info *oldinfop)
311{
312 /* if there is old info then this was a move and the old parent has to be
313 informed */
314 int count = oldinfop ? 2 : 1;
315 fileobj_sync_parent(&(const struct file_base_info *[])
316 { oldinfop, stream->infop }[2 - count],
317 count);
318}
319
320/* informs manager than directory entries have been updated */
321void fileobj_fileop_sync(struct filestr_base *stream)
322{
323 fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
324}
325
326/* inform manager that file has been truncated */
327void fileobj_fileop_truncate(struct filestr_base *stream)
328{
329 /* let caller update internal info */
330 FOR_EACH_STREAM(FIRST, (struct fileobj_binding *)stream->bindp, s)
331 ftruncate_internal_callback(stream, s);
332}
333
334/* query for the pointer to the size storage for the file object */
335file_size_t * fileobj_get_sizep(const struct filestr_base *stream)
336{
337 if (!stream->bindp)
338 return NULL;
339
340 return &((struct fileobj_binding *)stream->bindp)->size;
341}
342
343/* query manager bitflags for the file object */
344unsigned int fileobj_get_flags(const struct filestr_base *stream)
345{
346 if (!stream->bindp)
347 return 0;
348
349 return ((struct fileobj_binding *)stream->bindp)->flags;
350}
351
352/* change manager bitflags for the file object */
353void fileobj_change_flags(struct filestr_base *stream,
354 unsigned int flags, unsigned int mask)
355{
356 struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
357 if (fobp)
358 fobp->flags = (fobp->flags & ~mask) | (flags & mask);
359}
360
361/* mark all open streams on a device as "nonexistant" */
362void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
363{
364 /* right now, there is nothing else to be freed when marking a descriptor
365 as "nonexistant" but a callback could be added if that changes */
366 FOR_EACH_VOLUME(volume, v)
367 {
368 struct fileobj_binding *fobp;
369 while ((fobp = BINDING_FIRST(BUSY, v)))
370 {
371 struct filestr_base *s;
372 while ((s = STREAM_FIRST(fobp)))
373 {
374 /* keep it "busy" to avoid races; any valid file/directory
375 descriptor returned by an open call should always be
376 closed by whomever opened it (of course!) */
377 fileop_onclose_internal(s);
378 s->flags = FV_NONEXIST;
379 }
380 }
381 }
382}
383
384/* one-time init at startup */
385void fileobj_mgr_init(void)
386{
387 for (unsigned int i = 0; i < NUM_VOLUMES; i++)
388 ll_init(BUSY_BINDINGS(i));
389
390 ll_init(FREE_BINDINGS());
391 for (unsigned int i = 0; i < MAX_FILEOBJS; i++)
392 {
393 mutex_init(&stream_mutexes[i]);
394 binding_add_to_free_list(&fobindings[i]);
395 }
396}