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