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) 2002 by Björn Stenberg
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 <stdio.h>
22#include <string.h>
23#include "config.h"
24#include "kernel.h"
25#include "storage.h"
26#include "debug.h"
27#include "disk_cache.h"
28#include "fileobj_mgr.h"
29#include "dir.h"
30#include "rb_namespace.h"
31#include "disk.h"
32#include "panic.h"
33
34#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR) && !defined(BOOTLOADER)
35#include "bootdata.h"
36#include "crc32.h"
37#endif
38
39#ifndef CONFIG_DEFAULT_PARTNUM
40#define CONFIG_DEFAULT_PARTNUM 0
41#endif
42
43#define disk_reader_lock() file_internal_lock_READER()
44#define disk_reader_unlock() file_internal_unlock_READER()
45#define disk_writer_lock() file_internal_lock_WRITER()
46#define disk_writer_unlock() file_internal_unlock_WRITER()
47
48/* "MBR" Partition table entry layout:
49 -----------------------
50 0: 0x80 - active
51 1: starting head
52 2: starting sector
53 3: starting cylinder
54 4: partition type
55 5: end head
56 6: end sector
57 7: end cylinder
58 8-11: starting sector (LBA)
59 12-15: nr of sectors in partition
60*/
61
62#define BYTES2INT64(array, pos) \
63 (((uint64_t)array[pos+0] << 0) | \
64 ((uint64_t)array[pos+1] << 8) | \
65 ((uint64_t)array[pos+2] << 16) | \
66 ((uint64_t)array[pos+3] << 24) | \
67 ((uint64_t)array[pos+4] << 32) | \
68 ((uint64_t)array[pos+5] << 40) | \
69 ((uint64_t)array[pos+6] << 48) | \
70 ((uint64_t)array[pos+7] << 56) )
71
72#define BYTES2INT32(array, pos) \
73 (((uint32_t)array[pos+0] << 0) | \
74 ((uint32_t)array[pos+1] << 8) | \
75 ((uint32_t)array[pos+2] << 16) | \
76 ((uint32_t)array[pos+3] << 24))
77
78#define BYTES2INT16(array, pos) \
79 (((uint16_t)array[pos+0] << 0) | \
80 ((uint16_t)array[pos+1] << 8))
81
82static struct partinfo part[NUM_DRIVES*MAX_PARTITIONS_PER_DRIVE];
83static struct volumeinfo volumes[NUM_VOLUMES];
84
85/* check if the entry points to a free volume */
86static bool is_free_volume(const struct volumeinfo *vi)
87{
88 return vi->drive < 0;
89}
90
91/* mark a volume entry as free */
92static void mark_free_volume(struct volumeinfo *vi)
93{
94 vi->drive = -1;
95 vi->partition = -1;
96}
97
98static int get_free_volume(void)
99{
100 for (int i = 0; i < NUM_VOLUMES; i++)
101 if (is_free_volume(&volumes[i]))
102 return i;
103
104 return -1; /* none found */
105}
106
107static void init_volume(struct volumeinfo *vi, int drive, int part)
108{
109 vi->drive = drive;
110 vi->partition = part;
111}
112
113#ifdef MAX_VIRT_SECTOR_SIZE
114static uint8_t disk_sector_multiplier[NUM_DRIVES] =
115 { [0 ... NUM_DRIVES-1] = 1 };
116
117int disk_get_sector_multiplier(IF_MD_NONVOID(int drive))
118{
119 if (!CHECK_DRV(drive))
120 return 0;
121
122 disk_reader_lock();
123 int multiplier = disk_sector_multiplier[IF_MD_DRV(drive)];
124 disk_reader_unlock();
125 return multiplier;
126}
127
128#ifdef DEFAULT_VIRT_SECTOR_SIZE
129void disk_set_sector_multiplier(IF_MD(int drive,) int mult)
130{
131 if (!CHECK_DRV(drive))
132 return;
133
134 disk_writer_lock();
135 disk_sector_multiplier[IF_MD_DRV(drive)] = mult;
136 disk_writer_unlock();
137}
138#endif /* DEFAULT_VIRT_SECTOR_SIZE */
139#endif /* MAX_VIRT_SECTOR_SIZE */
140
141#ifdef MAX_VARIABLE_LOG_SECTOR
142static uint16_t disk_log_sector_size[NUM_DRIVES] =
143 { [0 ... NUM_DRIVES-1] = SECTOR_SIZE }; /* Updated from STORAGE_INFO */
144int disk_get_log_sector_size(IF_MD_NONVOID(int drive))
145{
146 if (!CHECK_DRV(drive))
147 return 0;
148
149 disk_reader_lock();
150 int size = disk_log_sector_size[IF_MD_DRV(drive)];
151 disk_reader_unlock();
152 return size;
153}
154#define LOG_SECTOR_SIZE(__drive) disk_log_sector_size[IF_MD_DRV(__drive)]
155#else /* !MAX_VARIABLE_LOG_SECTOR */
156#define LOG_SECTOR_SIZE(__drive) SECTOR_SIZE
157#endif /* !MAX_VARIABLE_LOG_SECTOR */
158
159bool disk_init(IF_MD_NONVOID(int drive))
160{
161 if (!CHECK_DRV(drive))
162 return false; /* out of space in table */
163
164 unsigned char *sector = dc_get_buffer();
165 if (!sector)
166 return false;
167
168 /* Query logical sector size */
169 struct storage_info *info = (struct storage_info*) sector;
170 storage_get_info(IF_MD_DRV(drive), info);
171 sector_t num_sectors = info->num_sectors;
172#ifdef DEFAULT_VIRT_SECTOR_SIZE
173 unsigned int sector_size = info->sector_size;
174#endif
175
176#if (CONFIG_STORAGE & STORAGE_ATA)
177 disk_writer_lock();
178#ifdef MAX_VARIABLE_LOG_SECTOR
179 disk_log_sector_size[IF_MD_DRV(drive)] = info->sector_size;
180#endif
181 disk_writer_unlock();
182
183#ifdef MAX_VARIABLE_LOG_SECTOR
184 if (info->sector_size > MAX_VARIABLE_LOG_SECTOR || info->sector_size > DC_CACHE_BUFSIZE) {
185 panicf("Unsupported logical sector size: %d",
186 info->sector_size);
187 }
188#else /* !MAX_VARIABLE_LOG_SECTOR */
189 if (info->sector_size != SECTOR_SIZE) {
190 panicf("Unsupported logical sector size: %d",
191 info->sector_size);
192 }
193#endif /* !MAX_VARIABLE_LOG_SECTOR */
194#endif /* STORAGE_ATA */
195
196 memset(sector, 0, DC_CACHE_BUFSIZE);
197 storage_read_sectors(IF_MD(drive,) 0, 1, sector);
198
199 bool init = false;
200
201 /* check that the boot sector is initialized */
202 if (BYTES2INT16(sector, 510) == 0xaa55)
203 {
204 /* For each drive, start at a different position, in order not to
205 destroy the first entry of drive 0. That one is needed to calculate
206 config sector position. */
207 struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
208 uint8_t is_gpt = 0;
209
210 disk_writer_lock();
211
212 /* parse partitions */
213 for (int i = 0; i < MAX_PARTITIONS_PER_DRIVE && i < 4; i++)
214 {
215 unsigned char* ptr = sector + 0x1be + 16*i;
216 pinfo[i].type = ptr[4];
217 pinfo[i].start = BYTES2INT32(ptr, 8);
218 pinfo[i].size = BYTES2INT32(ptr, 12);
219
220 DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
221 i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
222
223 /* extended? */
224 if ( pinfo[i].type == 0x05 || pinfo[i].type == 0x0f ) {
225 /* not handled yet */
226 }
227
228 if (pinfo[i].type == PARTITION_TYPE_GPT_GUARD) {
229 is_gpt = 1;
230 }
231
232#if 0 // Currently done in disk_mount() upon successful mount only
233 /* Auto-correct partition entries */
234 if (i == 0 && pinfo[i].type == 0 &&
235 pinfo[i].start != 0 && pinfo[i].size != 0) {
236 pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
237 // XXX consider correcting MBR and writing sector back?
238 }
239#endif
240 }
241
242 while (is_gpt) {
243 /* Re-start partition parsing using GPT */
244 uint64_t part_lba;
245 uint32_t part_entry_size;
246 uint32_t part_entries = 0;
247 unsigned char* ptr = sector;
248
249#ifdef DEFAULT_VIRT_SECTOR_SIZE
250 const sector_t try_gpt[4] = { 1, num_sectors - 1,
251 (DEFAULT_VIRT_SECTOR_SIZE / sector_size),
252 (num_sectors / (DEFAULT_VIRT_SECTOR_SIZE / sector_size)) - 1
253 };
254
255#else
256 sector_t try_gpt[2] = { 1, num_sectors - 1 };
257#endif
258
259 for (unsigned int i = 0 ; i < (sizeof(try_gpt) / sizeof(try_gpt[0])) ; i++) {
260 storage_read_sectors(IF_MD(drive,) try_gpt[i], 1, sector);
261 part_lba = BYTES2INT64(ptr, 0);
262 if (part_lba == 0x5452415020494645ULL) {
263 part_entries = 1;
264#ifdef MAX_VIRT_SECTOR_SIZE
265 if (i >= 2)
266 disk_sector_multiplier[IF_MD_DRV(drive)] = try_gpt[2];
267#endif
268 break;
269 }
270 }
271 if (!part_entries) {
272 DEBUGF("GPT: Invalid signature\n");
273 break;
274 }
275
276 part_entry_size = BYTES2INT32(ptr, 8);
277 if (part_entry_size != 0x00010000) {
278 DEBUGF("GPT: Invalid version\n");
279 break;
280 }
281 part_entry_size = BYTES2INT32(ptr, 12);
282 if (part_entry_size != 0x5c) {
283 DEBUGF("GPT: Invalid header size\n");
284 break;
285 }
286 // XXX checksum header -- u32 @ offset 16
287 part_entry_size = BYTES2INT32(ptr, 24);
288 if (part_entry_size != 1) {
289 DEBUGF("GPT: Invalid header LBA\n");
290 break;
291 }
292
293 part_lba = BYTES2INT64(ptr, 72);
294 part_entries = BYTES2INT32(ptr, 80);
295 part_entry_size = BYTES2INT32(ptr, 84);
296
297 int part = 0;
298reload:
299 storage_read_sectors(IF_MD(drive,) part_lba, 1, sector);
300 uint8_t *pptr = ptr;
301 while (part < MAX_PARTITIONS_PER_DRIVE && part_entries) {
302 if (pptr - ptr >= LOG_SECTOR_SIZE(drive)) {
303 part_lba++;
304 goto reload;
305 }
306
307 /* Parse GPT entry. We only care about the "General Data" type, ie:
308 EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
309 LE32 LE16 LE16 BE16 BE16
310 */
311 uint64_t tmp;
312 tmp = BYTES2INT32(pptr, 0);
313 if (tmp != 0xEBD0A0A2)
314 goto skip;
315 tmp = BYTES2INT16(pptr, 4);
316 if (tmp != 0xB9E5)
317 goto skip;
318 tmp = BYTES2INT16(pptr, 6);
319 if (tmp != 0x4433)
320 goto skip;
321 if (pptr[8] != 0x87 || pptr[9] != 0xc0)
322 goto skip;
323 if (pptr[10] != 0x68 || pptr[11] != 0xb6 || pptr[12] != 0xb7 ||
324 pptr[13] != 0x26 || pptr[14] != 0x99 || pptr[15] != 0xc7)
325 goto skip;
326
327 tmp = BYTES2INT64(pptr, 48); /* Flags */
328 if (tmp) {
329 DEBUGF("GPT: Skip parition with flags\n");
330 goto skip; /* Any flag makes us ignore this */
331 }
332 tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
333#ifndef STORAGE_64BIT_SECTOR
334 if (tmp > UINT32_MAX) {
335 DEBUGF("GPT: partition starts after 2TiB mark\n");
336 goto skip;
337 }
338#endif
339 if (tmp < 34) {
340 DEBUGF("GPT: Invalid start LBA\n");
341 goto skip;
342 }
343 pinfo[part].start = tmp;
344 tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
345#ifndef STORAGE_64BIT_SECTOR
346 if (tmp > UINT32_MAX) {
347 DEBUGF("GPT: partition ends after 2TiB mark\n");
348 goto skip;
349 }
350#endif
351 if (tmp <= pinfo[part].start) {
352 DEBUGF("GPT: Invalid end LBA\n");
353 goto skip;
354 }
355 pinfo[part].size = tmp - pinfo[part].start + 1;
356 pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
357
358 DEBUGF("GPart%d: start: %016lx size: %016lx\n",
359 part,pinfo[part].start,pinfo[part].size);
360 part++;
361
362 skip:
363 pptr += part_entry_size;
364 part_entries--;
365 }
366
367 is_gpt = 0; /* To break out of the loop */
368 }
369 disk_writer_unlock();
370
371 init = true;
372 }
373 else
374 {
375 DEBUGF("Bad boot sector signature\n");
376 }
377
378 dc_release_buffer(sector);
379 return init;
380}
381
382bool disk_partinfo(int partition, struct partinfo *info)
383{
384 if (partition < 0 || partition >= (int)ARRAYLEN(part) || !info)
385 return false;
386
387 disk_reader_lock();
388 *info = part[partition];
389 disk_reader_unlock();
390 return true;
391}
392
393int disk_mount(int drive)
394{
395 int mounted = 0; /* reset partition-on-drive flag */
396
397 disk_writer_lock();
398
399 int volume = get_free_volume();
400
401 if (volume < 0)
402 {
403 DEBUGF("No Free Volumes\n");
404 disk_writer_unlock();
405 return 0;
406 }
407
408 if (!disk_init(IF_MD(drive)))
409 {
410 disk_writer_unlock();
411 return 0;
412 }
413
414 struct partinfo *pinfo = &part[IF_MD_DRV(drive)*MAX_PARTITIONS_PER_DRIVE];
415#ifdef MAX_VIRT_SECTOR_SIZE
416 disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
417#endif
418
419 /* try "superfloppy" mode */
420 DEBUGF("Trying to mount sector 0.\n");
421
422 if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
423 {
424#ifdef MAX_VIRT_SECTOR_SIZE
425 disk_sector_multiplier[drive] = fat_get_bytes_per_sector(IF_MV(volume)) / LOG_SECTOR_SIZE(drive);
426#endif
427 mounted = 1;
428 init_volume(&volumes[volume], drive, 0);
429 volume_onmount_internal(IF_MV(volume));
430
431 struct storage_info info;
432 storage_get_info(drive, &info);
433
434 pinfo[0].type = PARTITION_TYPE_FAT32_LBA;
435 pinfo[0].start = 0;
436 pinfo[0].size = info.num_sectors;
437 }
438
439 if (mounted == 0 && volume != -1) /* not a "superfloppy"? */
440 {
441 for (int i = CONFIG_DEFAULT_PARTNUM;
442 volume != -1 && i < MAX_PARTITIONS_PER_DRIVE && mounted < NUM_VOLUMES_PER_DRIVE;
443 i++)
444 {
445 if (pinfo[i].type == 0x05 ||
446 pinfo[i].type == 0x0f ||
447 (i != 0 && pinfo[i].type == 0))
448 continue; /* skip free/extended partitions */
449
450 DEBUGF("Trying to mount partition %d.\n", i);
451
452#ifdef MAX_VIRT_SECTOR_SIZE
453 for (int j = 1; j <= (MAX_VIRT_SECTOR_SIZE/LOG_SECTOR_SIZE(drive)); j <<= 1)
454 {
455 if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start * j))
456 {
457 pinfo[i].start *= j;
458 pinfo[i].size *= j;
459 mounted++;
460 init_volume(&volumes[volume], drive, i);
461 disk_sector_multiplier[drive] = j;
462 volume_onmount_internal(IF_MV(volume));
463 volume = get_free_volume(); /* prepare next entry */
464 if (pinfo[i].type == 0) {
465 pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
466 // XXX write the sector back.?
467 }
468 break;
469 }
470 }
471#else /* ndef MAX_VIRT_SECTOR_SIZE */
472 if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start))
473 {
474 mounted++;
475 init_volume(&volumes[volume], drive, i);
476 volume_onmount_internal(IF_MV(volume));
477 volume = get_free_volume(); /* prepare next entry */
478 if (pinfo[i].type == 0) {
479 pinfo[i].type = PARTITION_TYPE_FAT32_LBA;
480 // XXX write the sector back.?
481 }
482 }
483#endif /* MAX_VIRT_SECTOR_SIZE */
484 }
485
486#if defined(MAX_VIRT_SECTOR_SIZE) && defined(MAX_PHYS_SECTOR_SIZE)
487 if (mounted)
488 ata_set_phys_sector_mult(disk_sector_multiplier[IF_MD_DRV(drive)]);
489#endif
490 }
491
492 disk_writer_unlock();
493 return mounted;
494}
495
496int disk_mount_all(void)
497{
498 int mounted = 0;
499
500 disk_writer_lock();
501
502 /* reset all mounted partitions */
503 volume_onunmount_internal(IF_MV(-1));
504 fat_init();
505
506 /* mark all volumes as free */
507 for (int i = 0; i < NUM_VOLUMES; i++)
508 mark_free_volume(&volumes[i]);
509
510 for (int i = 0; i < NUM_DRIVES; i++)
511 {
512 #ifdef HAVE_HOTSWAP
513 if (storage_present(i))
514 #endif
515 mounted += disk_mount(i);
516 }
517
518 disk_writer_unlock();
519 return mounted;
520}
521
522int disk_unmount(int drive)
523{
524 if (!CHECK_DRV(drive))
525 return 0;
526
527 int unmounted = 0;
528
529 disk_writer_lock();
530
531 for (int i = 0; i < NUM_VOLUMES; i++)
532 {
533 struct volumeinfo *vi = &volumes[i];
534 /* unmount any volumes on the drive */
535 if (vi->drive == drive)
536 {
537 mark_free_volume(vi); /* FIXME: should do this after unmount? */
538 volume_onunmount_internal(IF_MV(i));
539 fat_unmount(IF_MV(i));
540 unmounted++;
541 }
542 }
543
544 disk_writer_unlock();
545 return unmounted;
546}
547
548int disk_unmount_all(void)
549{
550 int unmounted = 0;
551
552 disk_writer_lock();
553
554 volume_onunmount_internal(IF_MV(-1));
555
556 for (int i = 0; i < NUM_DRIVES; i++)
557 {
558 #ifdef HAVE_HOTSWAP
559 if (storage_present(i))
560 #endif
561 unmounted += disk_unmount(i);
562 }
563
564 disk_writer_unlock();
565 return unmounted;
566}
567
568bool disk_present(IF_MD_NONVOID(int drive))
569{
570 int rc = -1;
571
572 if (CHECK_DRV(drive))
573 {
574 void *sector = dc_get_buffer();
575 if (sector)
576 {
577 rc = storage_read_sectors(IF_MD(drive,) 0, 1, sector);
578 dc_release_buffer(sector);
579 }
580 }
581
582 return rc == 0;
583}
584
585
586/** Volume-centric functions **/
587
588void volume_recalc_free(IF_MV_NONVOID(int volume))
589{
590 if (!CHECK_VOL(volume))
591 return;
592
593 /* FIXME: this is crummy but the only way to ensure a correct freecount
594 if other threads are writing and changing the fsinfo; it is possible
595 to get multiple threads calling here and also writing and get correct
596 freespace counts, however a bit complicated to do; if thou desireth I
597 shall implement the concurrent version -- jethead71 */
598 disk_writer_lock();
599 fat_recalc_free(IF_MV(volume));
600 disk_writer_unlock();
601}
602
603unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume))
604{
605 if (!CHECK_VOL(volume))
606 return 0;
607
608 disk_reader_lock();
609 unsigned int clustersize = fat_get_cluster_size(IF_MV(volume));
610 disk_reader_unlock();
611 return clustersize;
612}
613
614void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
615{
616 disk_reader_lock();
617
618 if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep))
619 {
620 if (sizep) *sizep = 0;
621 if (freep) *freep = 0;
622 }
623
624 disk_reader_unlock();
625}
626
627#if defined (HAVE_HOTSWAP) || defined (HAVE_MULTIDRIVE) \
628 || defined (HAVE_DIRCACHE) || defined(HAVE_BOOTDATA)
629enum volume_info_type
630{
631#ifdef HAVE_HOTSWAP
632 VP_REMOVABLE,
633 VP_PRESENT,
634#endif
635#if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE)
636 VP_DRIVE,
637#endif
638 VP_PARTITION,
639};
640
641static int volume_properties(int volume, enum volume_info_type infotype)
642{
643 int res = -1;
644
645 disk_reader_lock();
646
647 if (CHECK_VOL(volume))
648 {
649 struct volumeinfo *vi = &volumes[volume];
650 switch (infotype)
651 {
652 #ifdef HAVE_HOTSWAP
653 case VP_REMOVABLE:
654 res = storage_removable(vi->drive) ? 1 : 0;
655 break;
656 case VP_PRESENT:
657 res = storage_present(vi->drive) ? 1 : 0;
658 break;
659 #endif
660 #if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE)
661 case VP_DRIVE:
662 res = vi->drive;
663 break;
664 #endif
665 case VP_PARTITION:
666 res = vi->partition;
667 break;
668 }
669 }
670
671 disk_reader_unlock();
672 return res;
673}
674
675#ifdef HAVE_HOTSWAP
676bool volume_removable(int volume)
677{
678 return volume_properties(volume, VP_REMOVABLE) > 0;
679}
680
681bool volume_present(int volume)
682{
683 return volume_properties(volume, VP_PRESENT) > 0;
684}
685#endif /* HAVE_HOTSWAP */
686
687#ifdef HAVE_MULTIDRIVE
688int volume_drive(int volume)
689{
690 return volume_properties(volume, VP_DRIVE);
691}
692#endif /* HAVE_MULTIDRIVE */
693
694int volume_partition(int volume)
695{
696 return volume_properties(volume, VP_PARTITION);
697}
698
699#ifdef HAVE_DIRCACHE
700bool volume_ismounted(IF_MV_NONVOID(int volume))
701{
702 return volume_properties(IF_MV_VOL(volume), VP_DRIVE) >= 0;
703}
704#endif /* HAVE_DIRCACHE */
705
706
707#endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE || HAVE_BOOTDATA */