Releasing debian version 4.04+dfsg-9.
[syslinux-debian/hramrach.git] / core / fs / iso9660 / iso9660.c
blob3cd3ac46e0ac6d1d744ddd70f591f62f35bc4237
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 "iso9660_fs.h"
11 /* Convert to lower case string */
12 static inline char iso_tolower(char c)
14 if (c >= 'A' && c <= 'Z')
15 c += 0x20;
17 return c;
20 static struct inode *new_iso_inode(struct fs_info *fs)
22 return alloc_inode(fs, 0, sizeof(struct iso9660_pvt_inode));
25 static inline struct iso_sb_info *ISO_SB(struct fs_info *fs)
27 return fs->fs_info;
30 static size_t iso_convert_name(char *dst, const char *src, int len)
32 char *p = dst;
33 char c;
35 if (len == 1) {
36 switch (*src) {
37 case 1:
38 *p++ = '.';
39 /* fall through */
40 case 0:
41 *p++ = '.';
42 goto done;
43 default:
44 /* nothing special */
45 break;
49 while (len-- && (c = *src++)) {
50 if (c == ';') /* Remove any filename version suffix */
51 break;
52 *p++ = iso_tolower(c);
55 /* Then remove any terminal dots */
56 while (p > dst+1 && p[-1] == '.')
57 p--;
59 done:
60 *p = '\0';
61 return p - dst;
64 /*
65 * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match.
67 static bool iso_compare_name(const char *de_name, size_t len,
68 const char *file_name)
70 char iso_file_name[256];
71 char *p = iso_file_name;
72 char c1, c2;
73 int i;
75 i = iso_convert_name(iso_file_name, de_name, len);
76 (void)i;
77 dprintf("Compare: \"%s\" to \"%s\" (len %zu)\n",
78 file_name, iso_file_name, i);
80 do {
81 c1 = *p++;
82 c2 = iso_tolower(*file_name++);
84 /* compare equal except for case? */
85 if (c1 != c2)
86 return false;
87 } while (c1);
89 return true;
93 * Find a entry in the specified dir with name _dname_.
95 static const struct iso_dir_entry *
96 iso_find_entry(const char *dname, struct inode *inode)
98 struct fs_info *fs = inode->fs;
99 block_t dir_block = PVT(inode)->lba;
100 int i = 0, offset = 0;
101 const char *de_name;
102 int de_name_len, de_len;
103 const struct iso_dir_entry *de;
104 const char *data = NULL;
106 dprintf("iso_find_entry: \"%s\"\n", dname);
108 while (1) {
109 if (!data) {
110 dprintf("Getting block %d from block %llu\n", i, dir_block);
111 if (++i > inode->blocks)
112 return NULL; /* End of directory */
113 data = get_cache(fs->fs_dev, dir_block++);
114 offset = 0;
117 de = (const struct iso_dir_entry *)(data + offset);
118 de_len = de->length;
119 offset += de_len;
121 /* Make sure we have a full directory entry */
122 if (de_len < 33 || offset > BLOCK_SIZE(fs)) {
124 * Zero = end of sector, or corrupt directory entry
126 * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end
127 * in the Logical Sector in which it begins.
129 data = NULL;
130 continue;
133 de_name_len = de->name_len;
134 de_name = de->name;
135 if (iso_compare_name(de_name, de_name_len, dname)) {
136 dprintf("Found.\n");
137 return de;
142 static inline enum dirent_type get_inode_mode(uint8_t flags)
144 return (flags & 0x02) ? DT_DIR : DT_REG;
147 static struct inode *iso_get_inode(struct fs_info *fs,
148 const struct iso_dir_entry *de)
150 struct inode *inode = new_iso_inode(fs);
151 int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
153 if (!inode)
154 return NULL;
156 dprintf("Getting inode for: %.*s\n", de->name_len, de->name);
158 inode->mode = get_inode_mode(de->flags);
159 inode->size = de->size_le;
160 PVT(inode)->lba = de->extent_le;
161 inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs);
163 /* We have a single extent for all data */
164 inode->next_extent.pstart = (sector_t)de->extent_le << blktosec;
165 inode->next_extent.len = (sector_t)inode->blocks << blktosec;
167 return inode;
170 static struct inode *iso_iget_root(struct fs_info *fs)
172 const struct iso_dir_entry *root = &ISO_SB(fs)->root;
174 return iso_get_inode(fs, root);
177 static struct inode *iso_iget(const char *dname, struct inode *parent)
179 const struct iso_dir_entry *de;
181 dprintf("iso_iget %p %s\n", parent, dname);
183 de = iso_find_entry(dname, parent);
184 if (!de)
185 return NULL;
187 return iso_get_inode(parent->fs, de);
190 static int iso_readdir(struct file *file, struct dirent *dirent)
192 struct fs_info *fs = file->fs;
193 struct inode *inode = file->inode;
194 const struct iso_dir_entry *de;
195 const char *data = NULL;
197 while (1) {
198 size_t offset = file->offset & (BLOCK_SIZE(fs) - 1);
200 if (!data) {
201 uint32_t i = file->offset >> BLOCK_SHIFT(fs);
202 if (i >= inode->blocks)
203 return -1;
204 data = get_cache(fs->fs_dev, PVT(inode)->lba + i);
206 de = (const struct iso_dir_entry *)(data + offset);
208 if (de->length < 33 || offset + de->length > BLOCK_SIZE(fs)) {
209 file->offset = (file->offset + BLOCK_SIZE(fs))
210 & ~(BLOCK_SIZE(fs) - 1); /* Start of the next block */
211 data = NULL;
212 continue;
214 break;
217 dirent->d_ino = 0; /* Inode number is invalid to ISO fs */
218 dirent->d_off = file->offset;
219 dirent->d_type = get_inode_mode(de->flags);
220 dirent->d_reclen = offsetof(struct dirent, d_name) + 1 +
221 iso_convert_name(dirent->d_name, de->name, de->name_len);
223 file->offset += de->length; /* Update for next reading */
225 return 0;
228 /* Load the config file, return 1 if failed, or 0 */
229 static int iso_load_config(void)
231 static const char *search_directories[] = {
232 "/boot/isolinux",
233 "/isolinux",
234 "/boot/syslinux",
235 "/syslinux",
236 "/",
237 NULL
239 static const char *filenames[] = {
240 "isolinux.cfg",
241 "syslinux.cfg",
242 NULL
245 return search_config(search_directories, filenames);
248 static int iso_fs_init(struct fs_info *fs)
250 struct iso_sb_info *sbi;
251 char pvd[2048]; /* Primary Volume Descriptor */
252 uint32_t pvd_lba;
253 struct disk *disk = fs->fs_dev->disk;
254 int blktosec;
256 sbi = malloc(sizeof(*sbi));
257 if (!sbi) {
258 malloc_error("iso_sb_info structure");
259 return 1;
261 fs->fs_info = sbi;
264 * XXX: handling iso9660 in hybrid mode on top of a 4K-logical disk
265 * will really, really hurt...
267 fs->sector_shift = fs->fs_dev->disk->sector_shift;
268 fs->block_shift = 11; /* A CD-ROM block is always 2K */
269 fs->sector_size = 1 << fs->sector_shift;
270 fs->block_size = 1 << fs->block_shift;
271 blktosec = fs->block_shift - fs->sector_shift;
273 pvd_lba = iso_boot_info.pvd;
274 if (!pvd_lba)
275 pvd_lba = 16; /* Default if not otherwise defined */
277 disk->rdwr_sectors(disk, pvd, (sector_t)pvd_lba << blktosec,
278 1 << blktosec, false);
279 memcpy(&sbi->root, pvd + ROOT_DIR_OFFSET, sizeof(sbi->root));
281 /* Initialize the cache */
282 cache_init(fs->fs_dev, fs->block_shift);
284 return fs->block_shift;
288 const struct fs_ops iso_fs_ops = {
289 .fs_name = "iso",
290 .fs_flags = FS_USEMEM | FS_THISIND,
291 .fs_init = iso_fs_init,
292 .searchdir = NULL,
293 .getfssec = generic_getfssec,
294 .close_file = generic_close_file,
295 .mangle_name = generic_mangle_name,
296 .load_config = iso_load_config,
297 .iget_root = iso_iget_root,
298 .iget = iso_iget,
299 .readdir = iso_readdir,
300 .next_extent = no_next_extent,