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