• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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