71244a9afe32e0d806f3d10c62e260b939cfdcd1
[lunaix-os.git] / lunaix-os / kernel / fs / iso9660 / directory.c
1 #include <lunaix/dirent.h>
2 #include <lunaix/fs.h>
3 #include <lunaix/fs/iso9660.h>
4 #include <lunaix/mm/cake.h>
5 #include <lunaix/mm/valloc.h>
6 #include <lunaix/spike.h>
7
8 #include <klibc/string.h>
9
10 extern struct cake_pile* drec_cache_pile;
11
12 void
13 iso9660_fill_drecache(struct iso_drecache* cache, struct iso_drecord* drec)
14 {
15     *cache = (struct iso_drecache){ .data_size = drec->data_size.le,
16                                     .extent_addr = drec->extent_addr.le,
17                                     .flags = drec->flags,
18                                     .fu_size = drec->fu_sz ? drec->fu_sz : 1,
19                                     .gap_size = drec->gap_sz,
20                                     .xattr_len = drec->xattr_len };
21     u32_t l = drec->name.len;
22     while (l < (u32_t)-1 && drec->name.content[l--] != ';')
23         ;
24     l = (l + 1) ? l : drec->name.len;
25     l = MIN(l, ISO9660_IDLEN);
26     strncpy(cache->name_val, drec->name.content, l);
27     cache->name = HSTR(cache->name_val, l);
28     hstr_rehash(&cache->name, HSTR_FULL_HASH);
29 }
30
31 int
32 iso9660_setup_dnode(struct v_dnode* dnode, struct v_inode* inode)
33 {
34     if (!(inode->itype & VFS_IFDIR)) {
35         vfs_assign_inode(dnode, inode);
36         return 0;
37     }
38
39     int errno = 0;
40     struct device* dev = dnode->super_block->dev;
41     struct iso_inode* isoino = inode->data;
42     struct llist_header* lead = valloc(sizeof(*lead));
43     void* records = valloc(ISO9660_BLKSZ);
44     u32_t current_pos = -ISO9660_BLKSZ, max_pos = inode->fsize,
45           blk = inode->lb_addr * ISO9660_BLKSZ, blk_offset = (u32_t)-1;
46
47     llist_init_head(lead);
48
49     // As per 6.8.1, Directory structure shall NOT recorded in interleave mode.
50     do {
51         if (blk_offset >= ISO9660_BLKSZ - sizeof(struct iso_drecord)) {
52             current_pos += ISO9660_BLKSZ;
53             errno = dev->read(dev, records, blk + current_pos, ISO9660_BLKSZ);
54             if (errno < 0) {
55                 errno = EIO;
56                 goto done;
57             }
58             blk_offset = 0;
59         }
60
61         struct iso_drecord* drec;
62         struct iso_var_mdu* mdu = (struct iso_var_mdu*)(records + blk_offset);
63
64         if (!(drec = iso9660_get_drecord(mdu))) {
65             break;
66         }
67
68         // ignore the '.', '..' as we have built-in support
69         if (drec->name.len == 1) {
70             goto cont;
71         }
72
73         struct iso_drecache* cache = cake_grab(drec_cache_pile);
74
75         iso9660_fill_drecache(cache, drec);
76         llist_append(lead, &cache->caches);
77     cont:
78         blk_offset += mdu->len;
79     } while (current_pos + blk_offset < max_pos);
80
81     dnode->data = lead;
82     isoino->drecaches = lead;
83
84     vfs_assign_inode(dnode, inode);
85
86     errno = 0;
87
88 done:
89     vfree(records);
90     return errno;
91 }
92
93 int
94 iso9660_dir_lookup(struct v_inode* this, struct v_dnode* dnode)
95 {
96     struct iso_inode* isoino = this->data;
97     struct llist_header* lead = isoino->drecaches;
98     struct iso_drecache *pos, *n;
99
100     llist_for_each(pos, n, lead, caches)
101     {
102         if (HSTR_EQ(&dnode->name, &pos->name)) {
103             goto found;
104         }
105     }
106
107     return ENOENT;
108 found:
109     struct v_inode* inode = vfs_i_find(dnode->super_block, pos->extent_addr);
110
111     if (!inode) {
112         inode = vfs_i_alloc(dnode->super_block);
113         iso9660_fill_inode(inode, pos, pos->extent_addr);
114         vfs_i_addhash(inode);
115     }
116
117     return iso9660_setup_dnode(dnode, inode);
118 }
119
120 static int
121 __get_dtype(struct iso_drecache* pos)
122 {
123     if ((pos->flags & ISO_FDIR)) {
124         return DT_DIR;
125     } else {
126         return DT_FILE;
127     }
128 }
129
130 int
131 iso9660_readdir(struct v_file* file, struct dir_context* dctx)
132 {
133     struct llist_header* lead = file->dnode->data;
134     struct iso_drecache *pos, *n;
135     u32_t counter = dctx->index - 1;
136
137     llist_for_each(pos, n, lead, caches)
138     {
139         if (counter == (u32_t)-1 && !(pos->flags & ISO_FHIDDEN)) {
140             dctx->read_complete_callback(
141               dctx, pos->name_val, pos->name.len, __get_dtype(pos));
142             return 1;
143         }
144         counter--;
145     }
146     return 0;
147 }