feat: (iso9660) finishing up unmount
[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,
14                       struct iso_drecord* drec,
15                       u32_t drec_len)
16 {
17     *cache = (struct iso_drecache){ .data_size = drec->data_size.le,
18                                     .extent_addr = drec->extent_addr.le,
19                                     .flags = drec->flags,
20                                     .fu_size = drec->fu_sz ? drec->fu_sz : 1,
21                                     .gap_size = drec->gap_sz,
22                                     .xattr_len = drec->xattr_len };
23     u32_t padding = ((drec->name.len + sizeof(drec->name)) % 2) != 0;
24     u32_t su_offset = drec->name.len + sizeof(struct iso_drecord) + padding;
25     int su_len = drec_len - su_offset - 2, i = 0;
26
27     while (i < su_len) {
28         struct isosu_base* su_entry =
29           (struct isosu_base*)((void*)drec + su_offset + i);
30         switch (su_entry->signature) {
31             case ISORR_NM:
32                 i += isorr_parse_nm(cache, (void*)su_entry);
33                 break;
34             case ISORR_PX:
35                 i += isorr_parse_px(cache, (void*)su_entry);
36                 break;
37             case ISORR_TF:
38                 i += isorr_parse_tf(cache, (void*)su_entry);
39                 break;
40             case ISOSU_ST:
41                 goto done;
42             default:
43                 i += su_entry->length;
44                 break;
45         }
46     }
47
48 done:
49     if (!cache->name.len) {
50         // Load ISO9660 file id if no NM found.
51         u32_t l = drec->name.len;
52         while (l < (u32_t)-1 && drec->name.content[l--] != ';')
53             ;
54         l = (l + 1) ? l : drec->name.len;
55         l = MIN(l, ISO9660_IDLEN - 1);
56
57         strncpy(cache->name_val, drec->name.content, l);
58         cache->name = HSTR(cache->name_val, l);
59         hstr_rehash(&cache->name, HSTR_FULL_HASH);
60     }
61 }
62
63 int
64 iso9660_setup_dnode(struct v_dnode* dnode, struct v_inode* inode)
65 {
66     if (!(inode->itype & VFS_IFDIR)) {
67         vfs_assign_inode(dnode, inode);
68         return 0;
69     }
70
71     struct iso_inode* isoino = inode->data;
72     if (!llist_empty(&isoino->drecaches)) {
73         dnode->data = &isoino->drecaches;
74         vfs_assign_inode(dnode, inode);
75         return 0;
76     }
77
78     int errno = 0;
79     struct device* dev = dnode->super_block->dev;
80     void* records = valloc(ISO9660_BLKSZ);
81     u32_t current_pos = -ISO9660_BLKSZ, max_pos = inode->fsize,
82           blk = inode->lb_addr * ISO9660_BLKSZ, blk_offset = (u32_t)-1;
83
84     // As per 6.8.1, Directory structure shall NOT recorded in interleave mode.
85     do {
86         if (blk_offset >= ISO9660_BLKSZ - sizeof(struct iso_drecord)) {
87             current_pos += ISO9660_BLKSZ;
88             errno = dev->read(dev, records, blk + current_pos, ISO9660_BLKSZ);
89             if (errno < 0) {
90                 errno = EIO;
91                 goto done;
92             }
93             blk_offset = 0;
94         }
95
96         struct iso_drecord* drec;
97         struct iso_var_mdu* mdu = (struct iso_var_mdu*)(records + blk_offset);
98
99         if (!(drec = iso9660_get_drecord(mdu))) {
100             break;
101         }
102
103         // ignore the '.', '..' as we have built-in support
104         if (drec->name.len == 1) {
105             goto cont;
106         }
107
108         struct iso_drecache* cache = cake_grab(drec_cache_pile);
109
110         iso9660_fill_drecache(cache, drec, mdu->len);
111         llist_append(&isoino->drecaches, &cache->caches);
112     cont:
113         blk_offset += mdu->len;
114     } while (current_pos + blk_offset < max_pos);
115
116     dnode->data = &isoino->drecaches;
117
118     vfs_assign_inode(dnode, inode);
119
120     errno = 0;
121
122 done:
123     vfree(records);
124     return errno;
125 }
126
127 int
128 iso9660_dir_lookup(struct v_inode* this, struct v_dnode* dnode)
129 {
130     struct iso_inode* isoino = this->data;
131     struct iso_drecache *pos, *n;
132
133     llist_for_each(pos, n, &isoino->drecaches, caches)
134     {
135         if (HSTR_EQ(&dnode->name, &pos->name)) {
136             goto found;
137         }
138     }
139
140     return ENOENT;
141 found:
142     struct v_inode* inode = vfs_i_find(dnode->super_block, pos->extent_addr);
143
144     if (!inode) {
145         inode = vfs_i_alloc(dnode->super_block);
146         iso9660_fill_inode(inode, pos, pos->extent_addr);
147         vfs_i_addhash(inode);
148     }
149
150     return iso9660_setup_dnode(dnode, inode);
151 }
152
153 static int
154 __get_dtype(struct iso_drecache* pos)
155 {
156     if ((pos->flags & ISO_FDIR)) {
157         return DT_DIR;
158     } else {
159         return DT_FILE;
160     }
161 }
162
163 int
164 iso9660_readdir(struct v_file* file, struct dir_context* dctx)
165 {
166     struct llist_header* lead = file->dnode->data;
167     struct iso_drecache *pos, *n;
168     u32_t counter = dctx->index - 1;
169
170     llist_for_each(pos, n, lead, caches)
171     {
172         if (counter == (u32_t)-1 && !(pos->flags & ISO_FHIDDEN)) {
173             dctx->read_complete_callback(
174               dctx, pos->name_val, pos->name.len, __get_dtype(pos));
175             return 1;
176         }
177         counter--;
178     }
179     return 0;
180 }