1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/dirent.h>
5 #include <core.h>
6 #include <cache.h>
7 #include <disk.h>
8 #include <fs.h>
9 #include <stdlib.h>
10 #include "iso9660_fs.h"
11 #include "susp_rr.h"
12
13 /* Convert to lower case string */
iso_tolower(char c)14 static inline char iso_tolower(char c)
15 {
16 if (c >= 'A' && c <= 'Z')
17 c += 0x20;
18
19 return c;
20 }
21
new_iso_inode(struct fs_info * fs)22 static struct inode *new_iso_inode(struct fs_info *fs)
23 {
24 return alloc_inode(fs, 0, sizeof(struct iso9660_pvt_inode));
25 }
26
ISO_SB(struct fs_info * fs)27 static inline struct iso_sb_info *ISO_SB(struct fs_info *fs)
28 {
29 return fs->fs_info;
30 }
31
iso_convert_name(char * dst,const char * src,int len)32 static size_t iso_convert_name(char *dst, const char *src, int len)
33 {
34 char *p = dst;
35 char c;
36
37 if (len == 1) {
38 switch (*src) {
39 case 1:
40 *p++ = '.';
41 /* fall through */
42 case 0:
43 *p++ = '.';
44 goto done;
45 default:
46 /* nothing special */
47 break;
48 }
49 }
50
51 while (len-- && (c = *src++)) {
52 if (c == ';') /* Remove any filename version suffix */
53 break;
54 *p++ = iso_tolower(c);
55 }
56
57 /* Then remove any terminal dots */
58 while (p > dst+1 && p[-1] == '.')
59 p--;
60
61 done:
62 *p = '\0';
63 return p - dst;
64 }
65
66 /*
67 * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match.
68 */
iso_compare_name(const char * de_name,size_t len,const char * file_name)69 static bool iso_compare_name(const char *de_name, size_t len,
70 const char *file_name)
71 {
72 char iso_file_name[256];
73 char *p = iso_file_name;
74 char c1, c2;
75 int i;
76
77 i = iso_convert_name(iso_file_name, de_name, len);
78 (void)i;
79 dprintf("Compare: \"%s\" to \"%s\" (len %zu)\n",
80 file_name, iso_file_name, i);
81
82 do {
83 c1 = *p++;
84 c2 = iso_tolower(*file_name++);
85
86 /* compare equal except for case? */
87 if (c1 != c2)
88 return false;
89 } while (c1);
90
91 return true;
92 }
93
94 /*
95 * Find a entry in the specified dir with name _dname_.
96 */
97 static const struct iso_dir_entry *
iso_find_entry(const char * dname,struct inode * inode)98 iso_find_entry(const char *dname, struct inode *inode)
99 {
100 struct fs_info *fs = inode->fs;
101 block_t dir_block = PVT(inode)->lba;
102 int i = 0, offset = 0;
103 const char *de_name;
104 int de_name_len, de_len, rr_name_len, ret;
105 const struct iso_dir_entry *de;
106 const char *data = NULL;
107 char *rr_name = NULL;
108
109 dprintf("iso_find_entry: \"%s\"\n", dname);
110
111 while (1) {
112 if (!data) {
113 dprintf("Getting block %d from block %llu\n", i, dir_block);
114 if (++i > inode->blocks)
115 return NULL; /* End of directory */
116 data = get_cache(fs->fs_dev, dir_block++);
117 offset = 0;
118 }
119
120 de = (const struct iso_dir_entry *)(data + offset);
121 de_len = de->length;
122 offset += de_len;
123
124 /* Make sure we have a full directory entry */
125 if (de_len < 33 || offset > BLOCK_SIZE(fs)) {
126 /*
127 * Zero = end of sector, or corrupt directory entry
128 *
129 * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end
130 * in the Logical Sector in which it begins.
131 */
132 data = NULL;
133 continue;
134 }
135
136 /* Try to get Rock Ridge name */
137 ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &rr_name_len);
138 if (ret > 0) {
139 if (strcmp(rr_name, dname) == 0) {
140 dprintf("Found (by RR name).\n");
141 free(rr_name);
142 return de;
143 }
144 free(rr_name);
145 rr_name = NULL;
146 continue; /* Rock Ridge was valid and did not match */
147 }
148
149 /* Fall back to ISO name */
150 de_name_len = de->name_len;
151 de_name = de->name;
152 if (iso_compare_name(de_name, de_name_len, dname)) {
153 dprintf("Found (by ISO name).\n");
154 return de;
155 }
156 }
157 }
158
get_inode_mode(uint8_t flags)159 static inline enum dirent_type get_inode_mode(uint8_t flags)
160 {
161 return (flags & 0x02) ? DT_DIR : DT_REG;
162 }
163
iso_get_inode(struct fs_info * fs,const struct iso_dir_entry * de)164 static struct inode *iso_get_inode(struct fs_info *fs,
165 const struct iso_dir_entry *de)
166 {
167 struct inode *inode = new_iso_inode(fs);
168 int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
169
170 if (!inode)
171 return NULL;
172
173 dprintf("Getting inode for: %.*s\n", de->name_len, de->name);
174
175 inode->mode = get_inode_mode(de->flags);
176 inode->size = de->size_le;
177 PVT(inode)->lba = de->extent_le;
178 inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs);
179
180 /* We have a single extent for all data */
181 inode->next_extent.pstart = (sector_t)de->extent_le << blktosec;
182 inode->next_extent.len = (sector_t)inode->blocks << blktosec;
183
184 return inode;
185 }
186
iso_iget_root(struct fs_info * fs)187 static struct inode *iso_iget_root(struct fs_info *fs)
188 {
189 const struct iso_dir_entry *root = &ISO_SB(fs)->root;
190
191 return iso_get_inode(fs, root);
192 }
193
iso_iget(const char * dname,struct inode * parent)194 static struct inode *iso_iget(const char *dname, struct inode *parent)
195 {
196 const struct iso_dir_entry *de;
197
198 dprintf("iso_iget %p %s\n", parent, dname);
199
200 de = iso_find_entry(dname, parent);
201 if (!de)
202 return NULL;
203
204 return iso_get_inode(parent->fs, de);
205 }
206
iso_readdir(struct file * file,struct dirent * dirent)207 static int iso_readdir(struct file *file, struct dirent *dirent)
208 {
209 struct fs_info *fs = file->fs;
210 struct inode *inode = file->inode;
211 const struct iso_dir_entry *de;
212 const char *data = NULL;
213 char *rr_name = NULL;
214 int name_len, ret;
215
216 while (1) {
217 size_t offset = file->offset & (BLOCK_SIZE(fs) - 1);
218
219 if (!data) {
220 uint32_t i = file->offset >> BLOCK_SHIFT(fs);
221 if (i >= inode->blocks)
222 return -1;
223 data = get_cache(fs->fs_dev, PVT(inode)->lba + i);
224 }
225 de = (const struct iso_dir_entry *)(data + offset);
226
227 if (de->length < 33 || offset + de->length > BLOCK_SIZE(fs)) {
228 file->offset = (file->offset + BLOCK_SIZE(fs))
229 & ~(BLOCK_SIZE(fs) - 1); /* Start of the next block */
230 data = NULL;
231 continue;
232 }
233 break;
234 }
235
236 dirent->d_ino = 0; /* Inode number is invalid to ISO fs */
237 dirent->d_off = file->offset;
238 dirent->d_type = get_inode_mode(de->flags);
239
240 /* Try to get Rock Ridge name */
241 ret = susp_rr_get_nm(fs, (char *) de, &rr_name, &name_len);
242 if (ret > 0) {
243 memcpy(dirent->d_name, rr_name, name_len + 1);
244 free(rr_name);
245 rr_name = NULL;
246 } else {
247 name_len = iso_convert_name(dirent->d_name, de->name, de->name_len);
248 }
249
250 dirent->d_reclen = offsetof(struct dirent, d_name) + 1 + name_len;
251
252 file->offset += de->length; /* Update for next reading */
253
254 return 0;
255 }
256
257 /* Load the config file, return 1 if failed, or 0 */
iso_open_config(struct com32_filedata * filedata)258 static int iso_open_config(struct com32_filedata *filedata)
259 {
260 static const char *search_directories[] = {
261 "/boot/isolinux",
262 "/isolinux",
263 "/boot/syslinux",
264 "/syslinux",
265 "/",
266 NULL
267 };
268 static const char *filenames[] = {
269 "isolinux.cfg",
270 "syslinux.cfg",
271 NULL
272 };
273
274 return search_dirs(filedata, search_directories, filenames, ConfigName);
275 }
276
iso_fs_init(struct fs_info * fs)277 static int iso_fs_init(struct fs_info *fs)
278 {
279 struct iso_sb_info *sbi;
280 char pvd[2048]; /* Primary Volume Descriptor */
281 uint32_t pvd_lba;
282 struct disk *disk = fs->fs_dev->disk;
283 int blktosec;
284
285 sbi = malloc(sizeof(*sbi));
286 if (!sbi) {
287 malloc_error("iso_sb_info structure");
288 return 1;
289 }
290 fs->fs_info = sbi;
291
292 /*
293 * XXX: handling iso9660 in hybrid mode on top of a 4K-logical disk
294 * will really, really hurt...
295 */
296 fs->sector_shift = fs->fs_dev->disk->sector_shift;
297 fs->block_shift = 11; /* A CD-ROM block is always 2K */
298 fs->sector_size = 1 << fs->sector_shift;
299 fs->block_size = 1 << fs->block_shift;
300 blktosec = fs->block_shift - fs->sector_shift;
301
302 pvd_lba = iso_boot_info.pvd;
303 if (!pvd_lba)
304 pvd_lba = 16; /* Default if not otherwise defined */
305
306 disk->rdwr_sectors(disk, pvd, (sector_t)pvd_lba << blktosec,
307 1 << blktosec, false);
308 memcpy(&sbi->root, pvd + ROOT_DIR_OFFSET, sizeof(sbi->root));
309
310 /* Initialize the cache */
311 cache_init(fs->fs_dev, fs->block_shift);
312
313 /* Check for SP and ER in the first directory record of the root directory.
314 Set sbi->susp_skip and enable sbi->do_rr as appropriate.
315 */
316 susp_rr_check_signatures(fs, 1);
317
318 return fs->block_shift;
319 }
320
321
322 const struct fs_ops iso_fs_ops = {
323 .fs_name = "iso",
324 .fs_flags = FS_USEMEM | FS_THISIND,
325 .fs_init = iso_fs_init,
326 .searchdir = NULL,
327 .getfssec = generic_getfssec,
328 .close_file = generic_close_file,
329 .mangle_name = generic_mangle_name,
330 .open_config = iso_open_config,
331 .iget_root = iso_iget_root,
332 .iget = iso_iget,
333 .readdir = iso_readdir,
334 .next_extent = no_next_extent,
335 .fs_uuid = NULL,
336 };
337