A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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 <errno.h>
23#include "system.h"
24#include "debug.h"
25#include "panic.h"
26#include "pathfuncs.h"
27#include "disk_cache.h"
28#include "fileobj_mgr.h"
29#include "rb_namespace.h"
30#include "string-extra.h"
31#include "rbunicode.h"
32
33/* Define LOGF_ENABLE to enable logf output in this file */
34//#define LOGF_ENABLE
35#ifdef LOGF_ENABLE
36#include "logf.h"
37#undef DEBUGF
38#define DEBUGF logf
39#endif
40
41/** Internal common filesystem service functions **/
42
43/* for internal functions' scanning use to save quite a bit of stack space -
44 access must be serialized by the writer lock */
45#if defined(IAUDIO_M5) || CONFIG_CPU == IMX233
46/* otherwise, out of IRAM */
47struct fat_direntry dir_fatent;
48#else
49struct fat_direntry dir_fatent IBSS_ATTR;
50#endif
51
52struct mrsw_lock file_internal_mrsw SHAREDBSS_ATTR;
53
54
55/** File stream sector caching **/
56
57/* initialize a new cache structure */
58void file_cache_init(struct filestr_cache *cachep)
59{
60 cachep->buffer = NULL;
61 cachep->sector = INVALID_SECNUM;
62 cachep->flags = 0;
63}
64
65/* discard and mark the cache buffer as unused */
66void file_cache_reset(struct filestr_cache *cachep)
67{
68 cachep->sector = INVALID_SECNUM;
69 cachep->flags = 0;
70}
71
72/* allocate resources attached to the cache */
73void file_cache_alloc(struct filestr_cache *cachep)
74{
75 /* if this fails, it is a bug; check for leaks and that the cache has
76 enough buffers for the worst case */
77 if (!cachep->buffer && !(cachep->buffer = dc_get_buffer()))
78 panicf("file_cache_alloc - OOM");
79}
80
81/* free resources attached to the cache */
82void file_cache_free(struct filestr_cache *cachep)
83{
84 if (cachep)
85 {
86 if(cachep->buffer)
87 {
88 dc_release_buffer(cachep->buffer);
89 cachep->buffer = NULL;
90 }
91 file_cache_reset(cachep);
92 }
93}
94
95
96/** Stream base APIs **/
97
98static inline void filestr_clear(struct filestr_base *stream, unsigned int flags)
99{
100 stream->flags = flags;
101 stream->bindp = NULL;
102#if 0
103 stream->mtx = NULL;
104#endif
105}
106
107/* actually late-allocate the assigned cache */
108void filestr_alloc_cache(struct filestr_base *stream)
109{
110 file_cache_alloc(stream->cachep);
111}
112
113/* free the stream's cache buffer if it's its own */
114void filestr_free_cache(struct filestr_base *stream)
115{
116 if (stream->cachep == &stream->cache)
117 file_cache_free(stream->cachep);
118}
119
120/* assign a cache to the stream */
121void filestr_assign_cache(struct filestr_base *stream,
122 struct filestr_cache *cachep)
123{
124 if (cachep)
125 {
126 filestr_free_cache(stream);
127 stream->cachep = cachep;
128 }
129 else /* assign own cache */
130 {
131 file_cache_reset(&stream->cache);
132 stream->cachep = &stream->cache;
133 }
134}
135
136/* duplicate a cache into a stream's local cache */
137void filestr_copy_cache(struct filestr_base *stream,
138 struct filestr_cache *cachep)
139{
140 stream->cachep = &stream->cache;
141 stream->cache.sector = cachep->sector;
142 stream->cache.flags = cachep->flags;
143
144 if (cachep->buffer)
145 {
146 file_cache_alloc(&stream->cache);
147 memcpy(stream->cache.buffer, cachep->buffer, DC_CACHE_BUFSIZE);
148 }
149 else
150 {
151 file_cache_free(&stream->cache);
152 }
153}
154
155/* discard cache contents and invalidate it */
156void filestr_discard_cache(struct filestr_base *stream)
157{
158 file_cache_reset(stream->cachep);
159}
160
161/* Initialize the base descriptor */
162void filestr_base_init(struct filestr_base *stream)
163{
164 filestr_clear(stream, FD_VALID);
165 file_cache_init(&stream->cache);
166 stream->cachep = &stream->cache;
167}
168
169/* free base descriptor resources */
170void filestr_base_destroy(struct filestr_base *stream)
171{
172 filestr_clear(stream, 0);
173 filestr_free_cache(stream);
174}
175
176
177/** Internal directory service functions **/
178
179/* read the next directory entry and return its FS info */
180int uncached_readdir_internal(struct filestr_base *stream,
181 struct file_base_info *infop,
182 struct fat_direntry *fatent)
183{
184 return fat_readdir(&stream->fatstr, &infop->fatfile.e,
185 filestr_get_cache(stream), fatent);
186}
187
188/* rewind the FS directory to the beginning */
189void uncached_rewinddir_internal(struct file_base_info *infop)
190{
191 fat_rewinddir(&infop->fatfile.e);
192}
193
194/* check if the directory is empty (ie. only "." and/or ".." entries
195 exist at most) */
196int test_dir_empty_internal(struct filestr_base *stream)
197{
198 int rc;
199
200 struct file_base_info info;
201 fat_rewind(&stream->fatstr);
202 rewinddir_internal(&info);
203
204 while ((rc = readdir_internal(stream, &info, &dir_fatent)) > 0)
205 {
206 /* no OEM decoding is recessary for this simple check */
207 if (!is_dotdir_name(dir_fatent.name))
208 {
209 DEBUGF("Directory not empty\n");
210 FILE_ERROR_RETURN(ENOTEMPTY, -1);
211 }
212 }
213
214 if (rc < 0)
215 {
216 DEBUGF("I/O error checking directory: %d\n", rc);
217 FILE_ERROR_RETURN(EIO, rc * 10 - 2);
218 }
219
220 return 0;
221}
222
223/* iso decode the name to UTF-8 */
224void iso_decode_d_name(char *d_name)
225{
226#ifdef HAVE_FILESYSTEM_CODEPAGE
227 if (is_dotdir_name(d_name))
228 return;
229
230 char shortname[13];
231 /* this only gets called in the case of DOS (8.3) filenames */
232 size_t len = strlcpy(shortname, d_name, sizeof (shortname));
233 /* This MUST be the default codepage thus not something that could be
234 loaded on call */
235 iso_decode(shortname, d_name, -1, len + 1);
236#else
237 (void)d_name;
238#endif
239}
240
241#ifdef HAVE_DIRCACHE
242/* nullify all the fields of the struct dirent */
243void empty_dirent(struct DIRENT *entry)
244{
245 entry->d_name[0] = '\0';
246 entry->info.attr = 0;
247 entry->info.size = 0;
248 entry->info.wrtdate = 0;
249 entry->info.wrttime = 0;
250}
251
252/* fill the native dirinfo from the static dir_fatent */
253void fill_dirinfo_native(struct dirinfo_native *dinp)
254{
255 struct fat_direntry *fatent = get_dir_fatent();
256 dinp->attr = fatent->attr;
257 dinp->size = fatent->filesize;
258 dinp->wrtdate = fatent->wrtdate;
259 dinp->wrttime = fatent->wrttime;
260}
261#endif /* HAVE_DIRCACHE */
262
263int uncached_readdir_dirent(struct filestr_base *stream,
264 struct dirscan_info *scanp,
265 struct DIRENT *entry)
266{
267 struct fat_direntry fatent;
268 int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
269 filestr_get_cache(stream), &fatent);
270
271 /* FAT driver clears the struct fat_dirent if nothing is returned */
272 strcpy(entry->d_name, fatent.name);
273 entry->info.attr = fatent.attr;
274 entry->info.size = fatent.filesize;
275 entry->info.wrtdate = fatent.wrtdate;
276 entry->info.wrttime = fatent.wrttime;
277
278 return rc;
279}
280
281/* rewind the FS directory pointer */
282void uncached_rewinddir_dirent(struct dirscan_info *scanp)
283{
284 fat_rewinddir(&scanp->fatscan);
285}
286
287
288/** open_stream_internal() helpers and types **/
289
290struct pathwalk
291{
292 const char *path; /* current location in input path */
293 unsigned int callflags; /* callflags parameter */
294 struct path_component_info *compinfo; /* compinfo parameter */
295 file_size_t filesize; /* size of the file */
296};
297
298struct pathwalk_component
299{
300 struct file_base_info info; /* basic file information */
301 const char *name; /* component name location in path */
302 uint16_t length; /* length of name of component */
303 uint16_t attr; /* attributes of this component */
304 struct pathwalk_component *nextp; /* parent if in use else next free */
305};
306
307#define WALK_RC_NOT_FOUND 0 /* successfully not found (aid for file creation) */
308#define WALK_RC_FOUND 1 /* found and opened */
309#define WALK_RC_FOUND_ROOT 2 /* found and opened sys root */
310#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
311
312/* return another struct pathwalk_component from the pool, or NULL if the
313 pool is completely used */
314static void * pathwalk_comp_alloc_(struct pathwalk_component *parentp)
315{
316 /* static pool that goes to a depth of STATIC_COMP_NUM before allocating
317 elements from the stack */
318 static struct pathwalk_component aux_pathwalk[STATIC_PATHCOMP_NUM];
319 struct pathwalk_component *compp = NULL;
320
321 if (!parentp)
322 compp = &aux_pathwalk[0]; /* root */
323 else if (PTR_IN_ARRAY(aux_pathwalk, parentp, STATIC_PATHCOMP_NUM-1))
324 compp = parentp + 1;
325
326 return compp;
327}
328
329/* allocates components from the pool or stack depending upon the depth */
330#define pathwalk_comp_alloc(parentp) \
331 ({ \
332 void *__c = pathwalk_comp_alloc_(parentp); \
333 if (!__builtin_constant_p(parentp) && !__c) \
334 __c = alloca(sizeof (struct pathwalk_component)); \
335 (struct pathwalk_component *)__c; \
336 })
337
338/* fill in the details of the struct path_component_info for caller */
339static int fill_path_compinfo(struct pathwalk *walkp,
340 struct pathwalk_component *compp,
341 int rc)
342{
343 if (rc == -ENOENT)
344 {
345 /* this component wasn't found; see if more of them exist or path
346 has trailing separators; if it does, this component should be
347 interpreted as a directory even if it doesn't exist and it's the
348 final one; also, this has to be the last part or it's an error*/
349 const char *p = GOBBLE_PATH_SEPCH(walkp->path);
350 if (!*p)
351 {
352 if (p > walkp->path)
353 compp->attr |= ATTR_DIRECTORY;
354
355 rc = WALK_RC_NOT_FOUND; /* successfully not found */
356 }
357 }
358
359 if (rc >= 0)
360 {
361 struct path_component_info *compinfo = walkp->compinfo;
362 compinfo->name = compp->name;
363 compinfo->length = compp->length;
364 compinfo->attr = compp->attr;
365 compinfo->filesize = walkp->filesize;
366 if (walkp->callflags & FF_INFO)
367 compinfo->info = compp->info;
368 if (walkp->callflags & FF_PARENTINFO)
369 compinfo->parentinfo = (compp->nextp ?: compp)->info;
370 }
371
372 return rc;
373}
374
375/* open the final stream itself, if found */
376static int walk_open_info(struct pathwalk *walkp,
377 struct pathwalk_component *compp,
378 int rc,
379 struct filestr_base *stream)
380{
381 /* this may make adjustments to things; do it first */
382 if (walkp->compinfo)
383 rc = fill_path_compinfo(walkp, compp, rc);
384
385 if (rc < 0 || rc == WALK_RC_NOT_FOUND)
386 return rc;
387
388 unsigned int callflags = walkp->callflags;
389 bool isdir = compp->attr & ATTR_DIRECTORY;
390
391 /* type must match what is called for */
392 switch (callflags & FF_TYPEMASK)
393 {
394 case FF_FILE:
395 if (!isdir) break;
396 DEBUGF("File is a directory\n");
397 return -EISDIR;
398 case FF_DIR:
399 if (isdir) break;
400 DEBUGF("File is not a directory\n");
401 return -ENOTDIR;
402 /* FF_ANYTYPE: basically, ignore FF_FILE/FF_DIR */
403 }
404
405 /* FO_DIRECTORY must match type */
406 if (isdir)
407 callflags |= FO_DIRECTORY;
408 else
409 callflags &= ~FO_DIRECTORY;
410
411 /* make open official if not simply probing for presence - must do it here
412 or compp->info on stack will get destroyed before it was copied */
413 if (!(callflags & (FF_PROBE|FF_NOFS)))
414 fileop_onopen_internal(stream, &compp->info, callflags);
415 return compp->attr == ATTR_SYSTEM_ROOT ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
416}
417
418/* check the component against the prefix test info */
419static void walk_check_prefix(struct pathwalk *walkp,
420 struct pathwalk_component *compp)
421{
422 if (compp->attr & ATTR_PREFIX)
423 return;
424
425 if (!fat_file_is_same(&compp->info.fatfile,
426 &walkp->compinfo->prefixp->fatfile))
427 return;
428
429 compp->attr |= ATTR_PREFIX;
430}
431
432/* opens the component named by 'comp' in the directory 'parent' */
433static NO_INLINE int open_path_component(struct pathwalk *walkp,
434 struct pathwalk_component *compp,
435 struct filestr_base *stream)
436{
437 int rc;
438
439 /* create a null-terminated copy of the component name */
440 char *compname = strmemdupa(compp->name, compp->length);
441
442 unsigned int callflags = walkp->callflags;
443 struct pathwalk_component *parentp = compp->nextp;
444
445 /* children inherit the prefix coloring from the parent */
446 compp->attr = parentp->attr & ATTR_PREFIX;
447
448 /* most of the next would be abstracted elsewhere if doing other
449 filesystems */
450
451 /* scan parent for name; stream is converted to this parent */
452 file_cache_reset(stream->cachep);
453 stream->infop = &parentp->info;
454 fat_filestr_init(&stream->fatstr, &parentp->info.fatfile);
455 rewinddir_internal(&compp->info);
456
457 while ((rc = readdir_internal(stream, &compp->info, &dir_fatent)) > 0)
458 {
459 if (rc > 1 && !(callflags & FF_NOISO))
460 iso_decode_d_name(dir_fatent.name);
461
462 if (!strcasecmp(compname, dir_fatent.name))
463 break;
464 }
465
466 if (rc == 0)
467 {
468 DEBUGF("File/directory not found\n");
469 return -ENOENT;
470 }
471 else if (rc < 0)
472 {
473 DEBUGF("I/O error reading directory %d\n", rc);
474 return -EIO;
475 }
476
477 rc = fat_open(stream->fatstr.fatfilep, dir_fatent.firstcluster,
478 &compp->info.fatfile);
479 if (rc < 0)
480 {
481 DEBUGF("I/O error opening file/directory %s (%d)\n",
482 compname, rc);
483 return -EIO;
484 }
485
486 walkp->filesize = dir_fatent.filesize;
487 compp->attr |= dir_fatent.attr;
488
489 if (callflags & FF_CHECKPREFIX)
490 walk_check_prefix(walkp, compp);
491
492 return WALK_RC_FOUND;
493}
494
495/* parse a path component, open it and process the next */
496static NO_INLINE int
497walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
498 struct filestr_base *stream)
499{
500 int rc = WALK_RC_FOUND;
501
502 if (walkp->callflags & FF_CHECKPREFIX)
503 walk_check_prefix(walkp, compp);
504
505 /* alloca is used in a loop, but we reuse any blocks previously allocated
506 if we went up then back down; if the path takes us back to the root, then
507 everything is cleaned automatically */
508 struct pathwalk_component *freep = NULL;
509
510 const char *name;
511 ssize_t len;
512
513 while ((len = parse_path_component(&walkp->path, &name)))
514 {
515 /* whatever is to be a parent must be a directory */
516 if (!(compp->attr & ATTR_DIRECTORY))
517 return -ENOTDIR;
518
519 if (len > MAX_COMPNAME)
520 return -ENAMETOOLONG;
521
522 /* no filesystem is mounted here */
523 if (walkp->callflags & FF_NOFS)
524 return -ENOENT;
525
526 /* check for "." and ".." */
527 if (name[0] == '.')
528 {
529 if (len == 1)
530 continue; /* is "." */
531
532 if (len == 2 && name[1] == '.')
533 {
534 /* is ".." */
535 struct pathwalk_component *parentp = compp->nextp;
536
537 if (!parentp IF_MV( || parentp->attr == ATTR_SYSTEM_ROOT ))
538 return WALK_RC_CONT_AT_ROOT;
539
540 compp->nextp = freep;
541 freep = compp;
542 compp = parentp;
543 continue;
544 }
545 }
546
547 struct pathwalk_component *newp = freep;
548 if (!newp)
549 newp = pathwalk_comp_alloc(compp);
550 else
551 freep = freep->nextp;
552
553 newp->nextp = compp;
554 compp = newp;
555
556 compp->name = name;
557 compp->length = len;
558
559 rc = open_path_component(walkp, compp, stream);
560 if (rc < 0)
561 break; /* return info below */
562 }
563
564 return walk_open_info(walkp, compp, rc, stream);
565}
566
567/* open a stream given a path to the resource */
568int open_stream_internal(const char *path, unsigned int callflags,
569 struct filestr_base *stream,
570 struct path_component_info *compinfo)
571{
572 DEBUGF("%s(path=\"%s\",flg=%X,str=%p,compinfo=%p)\n", __func__,
573 path, callflags, stream, compinfo);
574 int rc;
575
576 filestr_base_init(stream);
577
578 if (!path_is_absolute(path))
579 {
580 /* while this supports relative components, there is currently no
581 current working directory concept at this level by which to
582 fully qualify the path (though that would not be excessively
583 difficult to add) */
584 DEBUGF("\"%s\" is not an absolute path\n"
585 "Only absolute paths currently supported.\n", path);
586 FILE_ERROR(path ? ENOENT : EFAULT, -1);
587 }
588
589 /* if !compinfo then these cannot be returned anyway */
590 if (!compinfo)
591 callflags &= ~(FF_INFO | FF_PARENTINFO | FF_CHECKPREFIX);
592
593 /* This lets it be passed quietly to directory scanning */
594 stream->flags |= callflags & FF_MASK;
595
596 struct pathwalk walk;
597 walk.path = path;
598 walk.callflags = callflags;
599 walk.compinfo = compinfo;
600 walk.filesize = 0;
601
602 struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
603 rootp->nextp = NULL;
604
605 while (1)
606 {
607 rc = ns_parse_root(walk.path, &rootp->name, &rootp->length);
608 if (rc < 0)
609 break;
610
611 rc = ns_open_root(IF_MV(rc,) &walk.callflags, &rootp->info, &rootp->attr);
612 if (rc < 0)
613 break;
614
615 walk.path = rootp->name + rootp->length;
616
617 rc = walk_path(&walk, rootp, stream);
618 if (rc != WALK_RC_CONT_AT_ROOT)
619 break;
620 }
621
622 if (rc >= 0)
623 {
624 /* FF_PROBE leaves nothing for caller to clean up */
625 if (walk.callflags & FF_PROBE)
626 filestr_base_destroy(stream);
627 }
628 else
629 {
630 /* utter, abject failure :`( */
631 DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
632 filestr_base_destroy(stream);
633 FILE_ERROR(-rc, -1);
634 }
635
636file_error:
637 return rc;
638}
639
640/* close the stream referenced by 'stream' */
641int close_stream_internal(struct filestr_base *stream)
642{
643 int rc;
644 unsigned int foflags = fileobj_get_flags(stream);
645
646 if ((foflags & (FO_SINGLE|FO_REMOVED)) == (FO_SINGLE|FO_REMOVED))
647 {
648 /* nothing is referencing it so now remove the file's data */
649 rc = fat_remove(&stream->infop->fatfile, FAT_RM_DATA);
650 if (rc < 0)
651 {
652 DEBUGF("I/O error removing file data: %d\n", rc);
653 FILE_ERROR(EIO, rc * 10 - 1);
654 }
655 }
656
657 rc = 0;
658file_error:
659 /* close no matter what */
660 fileop_onclose_internal(stream);
661 return rc;
662}
663
664/* create a new stream in the parent directory */
665int create_stream_internal(struct file_base_info *parentinfop,
666 const char *basename, size_t length,
667 unsigned int attr, unsigned int callflags,
668 struct filestr_base *stream)
669{
670 /* assumes an attempt was made beforehand to open *stream with
671 open_stream_internal() which returned zero (successfully not found),
672 so does not initialize it here */
673 const char * const name = strmemdupa(basename, length);
674 DEBUGF("Creating \"%s\"\n", name);
675
676 struct file_base_info info;
677 int rc = fat_create_file(&parentinfop->fatfile, name, attr,
678 &info.fatfile, get_dir_fatent_dircache());
679 if (rc < 0)
680 {
681 DEBUGF("Create failed: %d\n", rc);
682 FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 1);
683 }
684
685 /* dir_fatent is implicit arg */
686 fileop_oncreate_internal(stream, &info, callflags, parentinfop, name);
687 rc = 0;
688file_error:
689 return rc;
690}
691
692/* removes files and directories - back-end to remove() and rmdir() */
693int remove_stream_internal(const char *path, struct filestr_base *stream,
694 unsigned int callflags)
695{
696 /* Only FF_* flags should be in callflags */
697 int rc;
698
699 struct filestr_base opened_stream;
700 if (!stream)
701 stream = &opened_stream;
702
703 if (stream == &opened_stream)
704 {
705 /* no stream provided so open local one */
706 rc = open_stream_internal(path, callflags, stream, NULL);
707 if (rc < 0)
708 {
709 DEBUGF("Failed opening path: %d\n", rc);
710 FILE_ERROR(ERRNO, rc * 10 - 1);
711 }
712 }
713 /* else ignore the 'path' argument */
714
715 if (callflags & FF_DIR)
716 {
717 /* directory to be removed must be empty */
718 rc = test_dir_empty_internal(stream);
719 if (rc < 0)
720 FILE_ERROR(ERRNO, rc * 10 - 2);
721 }
722
723 /* save old info since fat_remove() will destroy the dir info */
724 struct file_base_info oldinfo = *stream->infop;
725 rc = fat_remove(&stream->infop->fatfile, FAT_RM_DIRENTRIES);
726 if (rc < 0)
727 {
728 DEBUGF("I/O error removing dir entries: %d\n", rc);
729 FILE_ERROR(EIO, rc * 10 - 3);
730 }
731
732 fileop_onremove_internal(stream, &oldinfo);
733
734 rc = 0;
735file_error:
736 if (stream == &opened_stream)
737 {
738 /* will do removal of data below if this is the only reference */
739 int rc2 = close_stream_internal(stream);
740 if (rc2 < 0 && rc >= 0)
741 {
742 rc = rc2 * 10 - 4;
743 DEBUGF("Success but failed closing stream: %d\n", rc);
744 }
745 }
746
747 return rc;
748}
749
750/* test file/directory existence with constraints */
751int test_stream_exists_internal(const char *path, unsigned int callflags)
752{
753 /* only FF_* flags should be in callflags */
754 struct filestr_base stream;
755 return open_stream_internal(path, callflags | FF_PROBE, &stream, NULL);
756}
757
758/* one-time init at startup */
759void filesystem_init(void)
760{
761 mrsw_init(&file_internal_mrsw);
762 dc_init();
763 fileobj_mgr_init();
764}