1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-02-25 GuEe-GUI the first version
9 */
10
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <rtservice.h>
14
15 #define DBG_TAG "dfs.iso9660"
16 #define DBG_LVL DBG_INFO
17 #include <rtdbg.h>
18
19 #include "dfs_iso9660.h"
20 #include <dfs.h>
21 #include <dfs_fs.h>
22 #include <dfs_file.h>
23 #include <posix/string.h>
24 #include <drivers/misc.h>
25 #include <drivers/byteorder.h>
26
27 #include <sys/time.h>
28
29 #define ISO9660_FSTYPE_DIR 0040000
30 #define ISO9660_FSTYPE_REG 0100000
31 #define ISO9660_FSTYPE_SYMLINK 0120000
32 #define ISO9660_FSTYPE_MASK 0170000
33
34 #define ISO9660_BLKSZ 2048
35
36 #define ISO9660_VOLDESC_BOOT 0
37 #define ISO9660_VOLDESC_PRIMARY 1
38 #define ISO9660_VOLDESC_SUPP 2
39 #define ISO9660_VOLDESC_PART 3
40 #define ISO9660_VOLDESC_END 255
41
42 rt_packed(struct iso9660_voldesc
43 {
44 rt_uint8_t type;
45 rt_uint8_t magic[5];
46 rt_uint8_t version;
47 });
48
49 rt_packed(struct iso9660_date2
50 {
51 rt_uint8_t year;
52 rt_uint8_t month;
53 rt_uint8_t day;
54 rt_uint8_t hour;
55 rt_uint8_t minute;
56 rt_uint8_t second;
57 rt_uint8_t offset;
58 });
59
60 /* Directory entry */
61 rt_packed(struct iso9660_dir
62 {
63 rt_uint8_t len;
64 rt_uint8_t ext_sectors;
65 rt_le32_t first_sector;
66 rt_le32_t first_sector_be;
67 rt_le32_t size;
68 rt_le32_t size_be;
69 struct iso9660_date2 mtime;
70 #define FLAG_TYPE_PLAIN 0
71 #define FLAG_TYPE_DIR 2
72 #define FLAG_TYPE 3
73 #define FLAG_MORE_EXTENTS 0x80
74 rt_uint8_t flags;
75 rt_uint8_t file_unit_size;
76 rt_uint8_t interleave_gap_size;
77 rt_le16_t vol_seq_num;
78 rt_le16_t vol_seq_num_be;
79 #define MAX_NAMELEN 255
80 rt_uint8_t namelen;
81 char name[0];
82 });
83
84 rt_packed(struct iso9660_date
85 {
86 rt_uint8_t year[4];
87 rt_uint8_t month[2];
88 rt_uint8_t day[2];
89 rt_uint8_t hour[2];
90 rt_uint8_t minute[2];
91 rt_uint8_t second[2];
92 rt_uint8_t hundredth[2];
93 rt_uint8_t offset;
94 });
95
96 /* Common volume descriptor */
97 rt_packed(struct iso9660_common_voldesc
98 {
99 struct iso9660_voldesc voldesc;
100 rt_uint8_t sysname[33];
101 rt_uint8_t volname[32];
102 rt_uint8_t unused2[8];
103 rt_le32_t vol_space_size_le;
104 rt_le32_t vol_space_size_be;
105 rt_uint8_t escape[32];
106 rt_le16_t vol_set_size_le;
107 rt_le16_t vol_set_size_be;
108 rt_le16_t vol_seq_num_le;
109 rt_le16_t vol_seq_num_be;
110 rt_le16_t logical_block_size_le;
111 rt_le16_t logical_block_size_be;
112 rt_le32_t path_table_size;
113 rt_le32_t path_table_size_be;
114 rt_le32_t path_table;
115 rt_le32_t path_table_be;
116 rt_uint8_t unused3[8];
117 struct iso9660_dir rootdir;
118 rt_uint8_t unused4[624];
119 struct iso9660_date created;
120 struct iso9660_date modified;
121 rt_uint8_t unused5[0 /* 1201 */];
122 });
123
124 struct iso9660
125 {
126 struct rt_device *dev;
127
128 rt_uint8_t joliet;
129 rt_uint8_t swap[ISO9660_BLKSZ];
130
131 struct iso9660_common_voldesc primary, supp;
132 };
133
134 struct iso9660_fd
135 {
136 struct iso9660 *iso;
137
138 struct iso9660_dir dirent;
139 };
140
141 struct iso9660_iterate
142 {
143 struct iso9660_fd *fd;
144
145 int i, index, count;
146 struct dirent *dirp;
147 };
148
iso9660_convert_string(char * dest,rt_uint16_t * src,int len)149 static void iso9660_convert_string(char *dest, rt_uint16_t *src, int len)
150 {
151 /* UTF16 to ASCII */
152 len >>= 1;
153
154 for (int i = 0; i < len; ++i)
155 {
156 rt_uint16_t utf16 = rt_be16_to_cpu(*src++);
157
158 if (utf16 < 0x80)
159 {
160 *dest++ = (rt_uint8_t)utf16;
161 }
162 else
163 {
164 *dest++ = '?';
165 }
166 }
167 *dest = '\0';
168 }
169
iso9660_convert_lower(char * dest,rt_uint8_t * src,int len)170 static void iso9660_convert_lower(char *dest, rt_uint8_t *src, int len)
171 {
172 for (int i = 0; i < len; ++i, ++src)
173 {
174 if (*src >= 'A' && *src <= 'Z')
175 {
176 *dest++ = *src - ('A' - 'a');
177 }
178 else
179 {
180 *dest++ = *src;
181 }
182 }
183
184 *dest = '\0';
185 }
186
iso9660_convert_unixtime(struct iso9660_date * date)187 static time_t iso9660_convert_unixtime(struct iso9660_date *date)
188 {
189 struct tm tm;
190
191 tm.tm_sec = (date->second[0] - '0') * 10 + (date->second[1] - '0');
192 tm.tm_min = (date->minute[0] - '0') * 10 + (date->minute[1] - '0');
193 tm.tm_hour = (date->hour[0] - '0') * 10 + (date->hour[1] - '0');
194 tm.tm_mday = (date->day[0] - '0') * 10 + (date->day[1] - '0');
195 tm.tm_mon = (date->month[0] - '0') * 10 + (date->month[1] - '0');
196 tm.tm_year = (date->year[0] - '0') * 1000 + (date->year[1] - '0') * 100 +
197 (date->year[2] - '0') * 10 + (date->year[3] - '0');
198 tm.tm_wday = 0;
199
200 return mktime(&tm);
201 }
202
iso9660_convert_unixtime2(struct iso9660_date2 * date)203 static time_t iso9660_convert_unixtime2(struct iso9660_date2 *date)
204 {
205 struct tm tm;
206
207 tm.tm_sec = date->second;
208 tm.tm_min = date->minute;
209 tm.tm_hour = date->hour;
210 tm.tm_mday = date->day;
211 tm.tm_mon = date->month;
212 tm.tm_year = date->year + 1900;
213 tm.tm_wday = 0;
214
215 return mktime(&tm);
216 }
217
iso9660_lookup(struct iso9660 * iso,const char * path,struct iso9660_iterate * it)218 static struct iso9660_fd *iso9660_lookup(struct iso9660 *iso, const char *path,
219 struct iso9660_iterate *it)
220 {
221 rt_uint32_t lba;
222 rt_size_t sz, len, namelen;
223 char sname[MAX_NAMELEN];
224 struct iso9660_fd *fd;
225 struct iso9660_dir *dirent;
226
227 if (it)
228 {
229 fd = it->fd;
230 iso = fd->iso;
231 dirent = &fd->dirent;
232
233 /* No next entry, always goon */
234 len = 1;
235 }
236 else
237 {
238 if (!(fd = rt_malloc(sizeof(*fd))))
239 {
240 return fd;
241 }
242
243 fd->iso = iso;
244 dirent = iso->joliet ? &iso->supp.rootdir : &iso->primary.rootdir;
245
246 if (!rt_strcmp(path, "/"))
247 {
248 rt_memcpy(&fd->dirent, dirent, sizeof(*dirent));
249 return fd;
250 }
251
252 /* Skip the first '/' */
253 ++path;
254 len = strchrnul(path, '/') - path;
255 }
256
257 lba = rt_le32_to_cpu(dirent->first_sector);
258 if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0)
259 {
260 goto _fail;
261 }
262 dirent = (void *)iso->swap;
263 sz = 0;
264
265 do {
266 /* Ignore "." and ".." */
267 do {
268 rt_uint32_t dlen = rt_le32_to_cpu(dirent->len);
269
270 dirent = (void *)dirent + dlen;
271 sz += dlen;
272
273 if (ISO9660_BLKSZ - sz < sizeof(*dirent))
274 {
275 /* Sector end, goto the next sector */
276 if (rt_device_read(iso->dev, ++lba, iso->swap, 1) <= 0)
277 {
278 goto _fail;
279 }
280 dirent = (void *)iso->swap;
281 sz = 0;
282 }
283
284 if (rt_le32_to_cpu(dirent->first_sector) == 0)
285 {
286 /* Is end, no found. */
287 goto _fail;
288 }
289 } while (dirent->name[0] >> 1 == 0 && rt_le32_to_cpu(dirent->namelen) == 1);
290
291 namelen = rt_le32_to_cpu(dirent->namelen);
292
293 if (iso->joliet)
294 {
295 iso9660_convert_string(sname, (rt_uint16_t *)dirent->name, namelen);
296 }
297 else
298 {
299 if (!(rt_le32_to_cpu(dirent->flags) & FLAG_TYPE_DIR))
300 {
301 /* Remove ";1" */
302 namelen -= 2;
303 }
304
305 iso9660_convert_lower(sname, (rt_uint8_t *)dirent->name, namelen);
306 }
307
308 if (it)
309 {
310 if (it->i < it->index)
311 {
312 goto _next;
313 }
314
315 if ((rt_le32_to_cpu(dirent->flags) & FLAG_TYPE) == FLAG_TYPE_DIR)
316 {
317 it->dirp->d_type = DT_DIR;
318 }
319 else
320 {
321 it->dirp->d_type = DT_REG;
322 }
323
324 it->dirp->d_namlen = namelen;
325 rt_strncpy(it->dirp->d_name, sname, namelen);
326 it->dirp->d_name[namelen] = '\0';
327 it->dirp->d_reclen = (rt_uint16_t)sizeof(struct dirent);
328
329 ++it->dirp;
330
331 _next:
332 ++it->i;
333
334 if (it->i - it->index >= it->count)
335 {
336 /* Iterate end */
337 return RT_NULL;
338 }
339
340 /* No next entry */
341 continue;
342 }
343
344 if (!rt_strncmp(sname, path, len))
345 {
346 /* The end of path, found ok */
347 if (!path[len])
348 {
349 rt_memcpy(&fd->dirent, dirent, sizeof(*dirent));
350 return fd;
351 }
352
353 /* Next entry */
354 lba = rt_le32_to_cpu(dirent->first_sector);
355 if (rt_device_read(iso->dev, lba, iso->swap, 1) <= 0)
356 {
357 goto _fail;
358 }
359 dirent = (void *)iso->swap;
360 sz = 0;
361
362 path += len + 1;
363 len = strchrnul(path, '/') - path;
364 }
365 } while (len);
366
367 _fail:
368 if (!it)
369 {
370 rt_free(fd);
371 }
372
373 return RT_NULL;
374 }
375
dfs_iso9660_open(struct dfs_file * fd)376 static int dfs_iso9660_open(struct dfs_file *fd)
377 {
378 struct iso9660 *iso = fd->vnode->fs->data;
379
380 fd->vnode->data = iso9660_lookup(iso, fd->vnode->path, RT_NULL);
381
382 return fd->vnode->data ? 0 : -EINVAL;
383 }
384
dfs_iso9660_close(struct dfs_file * fd)385 static int dfs_iso9660_close(struct dfs_file *fd)
386 {
387 struct iso9660_fd *iso_fd = fd->vnode->data;
388
389 rt_free(iso_fd);
390
391 return 0;
392 }
393
dfs_iso9660_read(struct dfs_file * fd,void * buf,size_t count)394 static ssize_t dfs_iso9660_read(struct dfs_file *fd, void *buf, size_t count)
395 {
396 rt_uint32_t pos;
397 void *buf_ptr;
398 ssize_t read_blk, toread_blk;
399 size_t rcount = 0, remain, size;
400 struct iso9660_fd *iso_fd = fd->vnode->data;
401 struct iso9660 *iso = iso_fd->iso;
402
403 if (fd->pos + count > rt_le32_to_cpu(iso_fd->dirent.size))
404 {
405 count = rt_le32_to_cpu(iso_fd->dirent.size) - fd->pos;
406 }
407 pos = rt_le32_to_cpu(iso_fd->dirent.first_sector);
408
409 /* Align to a sector */
410 if (fd->pos)
411 {
412 pos += fd->pos / ISO9660_BLKSZ;
413 remain = fd->pos & (ISO9660_BLKSZ - 1);
414
415 if (rt_device_read(iso->dev, pos, iso->swap, 1) <= 0)
416 {
417 return -EIO;
418 }
419
420 size = rt_min_t(size_t, ISO9660_BLKSZ - remain, count);
421 rt_memcpy(buf, &iso->swap[remain], size);
422 rcount += size;
423 count -= size;
424 buf += size;
425 pos += 1;
426 fd->pos += size;
427
428 if (!count)
429 {
430 goto _end;
431 }
432 }
433
434 remain = count & (ISO9660_BLKSZ - 1);
435 count = rt_max_t(size_t, count / ISO9660_BLKSZ, 1);
436
437 while ((ssize_t)count > 0)
438 {
439 if (count == 1)
440 {
441 buf_ptr = iso->swap;
442 toread_blk = 1;
443 }
444 else
445 {
446 buf_ptr = buf;
447 toread_blk = count;
448 }
449
450 read_blk = rt_device_read(iso->dev, pos, buf_ptr, toread_blk);
451
452 if (read_blk <= 0)
453 {
454 return (int)read_blk;
455 }
456
457 if (count == 1)
458 {
459 size = remain;
460 rt_memcpy(buf, iso->swap, size);
461 }
462 else
463 {
464 size = read_blk * ISO9660_BLKSZ;
465 }
466
467 rcount += size;
468 count -= read_blk;
469 buf += size;
470 pos += read_blk;
471 fd->pos += size;
472 }
473
474 _end:
475 return rcount;
476 }
477
dfs_iso9660_lseek(struct dfs_file * fd,off_t offset)478 static off_t dfs_iso9660_lseek(struct dfs_file *fd, off_t offset)
479 {
480 int ret = -EIO;
481
482 if (offset <= fd->vnode->size)
483 {
484 fd->pos = offset;
485 ret = fd->pos;
486 }
487
488 return ret;
489 }
490
dfs_iso9660_getdents(struct dfs_file * fd,struct dirent * dirp,uint32_t count)491 static int dfs_iso9660_getdents(struct dfs_file *fd, struct dirent *dirp, uint32_t count)
492 {
493 struct iso9660_iterate it;
494 struct iso9660_fd *iso_fd = fd->vnode->data;
495
496 count = (count / sizeof(struct dirent));
497
498 if (!count)
499 {
500 return -EINVAL;
501 }
502
503 it.fd = iso_fd;
504 it.i = 0;
505 it.index = fd->pos;
506 it.count = count;
507 it.dirp = dirp;
508
509 iso9660_lookup(RT_NULL, RT_NULL, &it);
510
511 count = it.i - it.index;
512 if (count > 0)
513 {
514 fd->pos += count;
515 }
516
517 count *= sizeof(struct dirent);
518
519 return count;
520 }
521
522 static const struct dfs_file_ops _iso9660_fops =
523 {
524 .open = dfs_iso9660_open,
525 .close = dfs_iso9660_close,
526 .read = dfs_iso9660_read,
527 .lseek = dfs_iso9660_lseek,
528 .getdents = dfs_iso9660_getdents,
529 };
530
dfs_iso9660_mount(struct dfs_filesystem * fs,unsigned long rwflag,const void * data)531 static int dfs_iso9660_mount(struct dfs_filesystem *fs,
532 unsigned long rwflag, const void *data)
533 {
534 int err;
535 struct iso9660 *iso;
536 struct iso9660_voldesc *voldesc;
537 struct rt_device_blk_geometry geometry;
538
539 if (!(iso = rt_malloc(sizeof(*iso))))
540 {
541 return -RT_ENOMEM;
542 }
543
544 iso->dev = fs->dev_id;
545 rt_device_control(iso->dev, RT_DEVICE_CTRL_BLK_GETGEOME, &geometry);
546
547 if (geometry.bytes_per_sector != ISO9660_BLKSZ)
548 {
549 LOG_E("%s: Logical block size = %d is not supported",
550 iso->dev->parent.name, geometry.bytes_per_sector);
551
552 err = -EINVAL;
553 goto _fail;
554 }
555
556 iso->primary.rootdir.first_sector = 0;
557 iso->supp.rootdir.first_sector = 0;
558
559 /* LBA 0-15 is the bootloader's information */
560 for (int lba = 16; ; ++lba)
561 {
562 if (rt_device_read(iso->dev, lba, &iso->swap, 1) <= 0)
563 {
564 err = -EIO;
565 goto _fail;
566 }
567
568 voldesc = (void *)iso->swap;
569
570 if (rt_strncmp((char *)voldesc->magic, "CD001", 5))
571 {
572 LOG_E("%s: Invalid magic \"%s\"", voldesc->magic);
573
574 err = -EINVAL;
575 goto _fail;
576 }
577
578 if (voldesc->type == ISO9660_VOLDESC_BOOT)
579 {
580 LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname);
581 LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname);
582 }
583 else if (voldesc->type == ISO9660_VOLDESC_PRIMARY)
584 {
585 iso->joliet = 0;
586 rt_memcpy(&iso->primary, &iso->swap, sizeof(iso->primary));
587 }
588 else if (voldesc->type == ISO9660_VOLDESC_SUPP)
589 {
590 rt_memcpy(&iso->supp, &iso->swap, sizeof(iso->supp));
591
592 if (iso->supp.escape[0] == 0x25 && iso->supp.escape[1] == 0x2f)
593 {
594 if (iso->supp.escape[2] == 0x40)
595 {
596 iso->joliet = 1;
597 }
598 else if (iso->supp.escape[2] == 0x43)
599 {
600 iso->joliet = 2;
601 }
602 else if (iso->supp.escape[2] == 0x45)
603 {
604 iso->joliet = 3;
605 }
606 else
607 {
608 continue;
609 }
610 }
611 }
612 else if (voldesc->type == ISO9660_VOLDESC_PART)
613 {
614 LOG_D("System Name: %s", ((struct iso9660_common_voldesc *)voldesc)->sysname);
615 LOG_D("Volume Name: %s", ((struct iso9660_common_voldesc *)voldesc)->volname);
616 }
617 else if (voldesc->type == ISO9660_VOLDESC_END)
618 {
619 break;
620 }
621 }
622
623 if (!iso->primary.rootdir.first_sector || !iso->supp.rootdir.first_sector)
624 {
625 LOG_E("No primary or secondary partition found");
626
627 err = -EINVAL;
628 goto _fail;
629 }
630
631 fs->data = iso;
632
633 return 0;
634
635 _fail:
636 rt_free(iso);
637
638 return err;
639 }
640
dfs_iso9660_unmount(struct dfs_filesystem * fs)641 static int dfs_iso9660_unmount(struct dfs_filesystem *fs)
642 {
643 struct iso9660 *iso = fs->data;
644
645 rt_free(iso);
646
647 return 0;
648 }
649
dfs_iso9660_stat(struct dfs_filesystem * fs,const char * filename,struct stat * st)650 static int dfs_iso9660_stat(struct dfs_filesystem *fs,
651 const char *filename, struct stat *st)
652 {
653 struct iso9660 *iso = fs->data;
654 struct iso9660_fd *fd = iso9660_lookup(iso, filename, RT_NULL);
655
656 if (!fd)
657 {
658 return -EINVAL;
659 }
660
661 st->st_dev = 0;
662 st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
663 S_IWUSR | S_IWGRP | S_IWOTH;
664
665 if ((fd->dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
666 {
667 st->st_mode &= ~S_IFREG;
668 st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
669 }
670
671 st->st_atime = iso9660_convert_unixtime(iso->joliet ?
672 &iso->supp.created : &iso->primary.created);
673 st->st_mtime = iso9660_convert_unixtime2(&fd->dirent.mtime);
674 st->st_size = rt_le32_to_cpu(fd->dirent.size);
675
676 rt_free(fd);
677
678 return 0;
679 }
680
681 static const struct dfs_filesystem_ops _iso9660 =
682 {
683 .name = "iso9660",
684 .flags = DFS_FS_FLAG_DEFAULT,
685 .fops = &_iso9660_fops,
686
687 .mount = dfs_iso9660_mount,
688 .unmount = dfs_iso9660_unmount,
689
690 .stat = dfs_iso9660_stat,
691 };
692
dfs_iso9660_init(void)693 int dfs_iso9660_init(void)
694 {
695 /* register iso9660 file system */
696 return dfs_register(&_iso9660);
697 }
698 INIT_COMPONENT_EXPORT(dfs_iso9660_init);
699