source: bootcd/isolinux/syslinux-6.03/core/fs/iso9660/iso9660.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 8.0 KB
Line 
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 */
14static inline char iso_tolower(char c)
15{
16    if (c >= 'A' && c <= 'Z')
17        c += 0x20;
18
19    return c;
20}
21
22static struct inode *new_iso_inode(struct fs_info *fs)
23{
24    return alloc_inode(fs, 0, sizeof(struct iso9660_pvt_inode));
25}
26
27static inline struct iso_sb_info *ISO_SB(struct fs_info *fs)
28{
29    return fs->fs_info;
30}
31
32static 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
61done:
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 */
69static 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 */
97static const struct iso_dir_entry *
98iso_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
159static inline enum dirent_type get_inode_mode(uint8_t flags)
160{
161    return (flags & 0x02) ? DT_DIR : DT_REG;
162}
163
164static 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
187static 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
194static 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
207static 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 */
258static 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
277static 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
322const 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};
Note: See TracBrowser for help on using the repository browser.