19bd314e9bd73a855b39584136791763c94a7cc6
[profile/ivi/syslinux.git] / core / fs / iso9660 / iso9660.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <core.h>
4 #include <cache.h>
5 #include <disk.h>
6 #include <fs.h>
7 #include "iso9660_fs.h"
8
9 static struct inode *new_iso_inode(void)
10 {
11     struct inode *inode = malloc(sizeof(*inode));
12     
13     if (!inode) {
14         malloc_error("inode structure in new_iso_inode");
15         return NULL;
16     }
17     memset(inode, 0, sizeof(*inode));
18     
19     inode->data = malloc(sizeof(uint32_t));
20     if (!inode) {
21         malloc_error("inode->data in new_iso_inode");
22         free(inode);
23         return NULL;
24     }
25     
26     return inode;
27 }
28
29
30 static void iso_close_file(struct file *file)
31 {
32     if (file->inode) {
33         file->offset = 0;
34         free_inode(file->inode);
35     }
36 }
37
38 static inline struct iso_sb_info * ISO_SB(struct fs_info *fs)
39 {
40     return fs->fs_info;
41 }
42
43 /*
44  * Mangle a filename pointed to by src into a buffer pointed
45  * to by dst; ends on encountering any whitespace.
46  * dst is preserved.
47  *
48  * This verifies that a filename is < FilENAME_MAX characters,
49  * doesn't contain whitespace, zero-pads the output buffer,
50  * and removes trailing dots and redumndant slashes, so "repe
51  * cmpsb" can do a compare, and the path-searching routine gets
52  * a bit of an easier job.
53  *
54  */
55 static void iso_mangle_name(char *dst, const char *src)
56 {
57     char *p = dst;
58     int i = FILENAME_MAX - 1;
59
60     while (not_whitespace(*src)) {
61         if ( *src == '/' ) {
62             if ( *(src+1) == '/' ) {
63                 i--;
64                 src++;
65                 continue;
66             }
67         }
68
69         *dst++ = *src ++;
70         i--;
71     }
72
73     while ( 1 ) {
74         if ( dst == p )
75             break;
76
77         if ( (*(dst-1) != '.') && (*(dst-1) != '/') )
78             break;
79
80         dst --;
81         i ++;
82     }
83
84     i ++;
85     for (; i > 0; i -- )
86         *dst++ = '\0';
87 }
88
89 static int iso_convert_name(char *dst, char *src, int len)
90 {
91     int i = 0;
92     char c;
93     
94     for (; i < len; i++) {
95         c = src[i];
96         if (!c)
97             break;
98         
99         /* remove ';1' in the end */
100         if (c == ';' && i == len - 2 && src[i + 1] == '1')
101             break;
102         /* convert others ';' to '.' */
103         if (c == ';')
104             c = '.';
105         *dst++ = c;
106     }
107     
108     /* Then remove the terminal dots */
109     while (*(dst - 1) == '.') {
110         if (i <= 2)
111             break;
112         dst--;
113         i--;
114     }
115     *dst = 0;
116     
117     return i;
118 }
119
120 /* 
121  * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match.
122  */
123 static int iso_compare_name(char *de_name, int len, char *file_name)
124 {
125     char iso_file_name[256];
126     char *p = iso_file_name;
127     char c1, c2;
128     int i;
129     
130     i = iso_convert_name(iso_file_name, de_name, len);
131     
132     if (i != (int)strlen(file_name))
133         return 0;
134     
135     while (i--) {
136         c1 = *p++;
137         c2 = *file_name++;
138         
139         /* convert to lower case */
140         c1 |= 0x20;
141         c2 |= 0x20;
142         if (c1 != c2)
143             return 0;
144     }
145     
146     return 1;
147 }
148
149 static inline int cdrom_read_blocks(struct disk *disk, void *buf, 
150                                     int block, int blocks)
151 {
152     return disk->rdwr_sectors(disk, buf, block, blocks, 0);
153 }
154
155 /*
156  * Get multiple clusters from a file, given the file pointer.
157  */
158 static uint32_t iso_getfssec(struct file *file, char *buf,
159                              int blocks, bool *have_more)
160 {
161     struct fs_info *fs = file->fs;
162     struct disk *disk = fs->fs_dev->disk;
163     uint32_t bytes_read = blocks << fs->block_shift;
164     uint32_t bytes_left = file->inode->size - file->offset;
165     uint32_t blocks_left = (bytes_left + BLOCK_SIZE(file->fs) - 1) 
166         >> file->fs->block_shift;
167     block_t block = *file->inode->data + (file->offset >> fs->block_shift);    
168     
169     if (blocks > blocks_left)
170         blocks = blocks_left;
171     cdrom_read_blocks(disk, buf, block, blocks);
172
173     if (bytes_read >= bytes_left) {
174         bytes_read = bytes_left;
175         *have_more = 0;
176     } else {
177         *have_more = 1;
178     }
179     
180     file->offset += bytes_read;
181     return bytes_read;
182 }
183
184 /*
185  * Find a entry in the specified dir with name _dname_.
186  */
187 static struct iso_dir_entry *iso_find_entry(char *dname, struct inode *inode)
188 {
189     block_t dir_block = *inode->data;
190     int i = 0, offset = 0;
191     char *de_name;
192     int de_name_len, de_len;
193     struct iso_dir_entry *de;
194     struct iso_dir_entry tmpde;
195     struct cache_struct *cs = NULL;
196     
197     while (1) {
198         if (!cs) {
199             if (++i > inode->blocks)
200                 return NULL;
201             cs = get_cache_block(this_fs->fs_dev, dir_block++);
202             de = (struct iso_dir_entry *)cs->data;
203             offset = 0;
204         }
205         de = (struct iso_dir_entry *)(cs->data + offset);
206         
207         de_len = de->length;
208         if (de_len == 0) {    /* move on to the next block */
209             cs = NULL;
210             continue;
211         }
212         offset += de_len;
213         
214         /* Make sure we have a full directory entry */
215         if (offset >= BLOCK_SIZE(this_fs)) {
216             int slop = de_len + BLOCK_SIZE(this_fs) - offset;
217             
218             memcpy(&tmpde, de, slop);
219             offset &= BLOCK_SIZE(this_fs) - 1;
220             if (offset) {
221                 if (++i > inode->blocks)
222                     return NULL;
223                 cs = get_cache_block(this_fs->fs_dev, dir_block++);
224                 memcpy((void *)&tmpde + slop, cs->data, offset);
225             }
226             de = &tmpde;
227         }
228         
229         if (de_len < 33) {
230             printf("Corrupted directory entry in sector %u\n", 
231                    (uint32_t)(dir_block - 1));
232             return NULL;
233         }
234         
235         de_name_len = de->name_len;
236         de_name = de->name;
237         /* Handling the special case ".' and '..' here */
238         if((de_name_len == 1) && (*de_name == 0)) {
239             de_name = ".";
240         } else if ((de_name_len == 1) && (*de_name == 1)) {
241             de_name ="..";
242             de_name_len = 2;
243         }
244         if (iso_compare_name(de_name, de_name_len, dname))
245             return de;
246     }
247 }
248
249 static inline int get_inode_mode(uint8_t flags)
250 {
251     if (flags & 0x02)
252         return I_DIR;
253     else
254         return I_FILE;
255 }
256
257 static struct inode *iso_get_inode(struct iso_dir_entry *de)
258 {
259     struct inode *inode = new_iso_inode();
260     
261     if (!inode)
262         return NULL;
263     inode->mode   = get_inode_mode(de->flags);
264     inode->size   = *(uint32_t *)de->size;
265     *inode->data  = *(uint32_t *)de->extent;
266     inode->blocks = (inode->size + BLOCK_SIZE(this_fs) - 1) 
267         >> this_fs->block_shift;
268     
269     return inode;
270 }
271
272
273 static struct inode *iso_iget_root(void)
274 {
275     struct inode *inode = new_iso_inode();
276     struct iso_dir_entry *root = &ISO_SB(this_fs)->root;
277     
278     if (!inode) 
279         return NULL;
280     
281     inode->mode   = I_DIR;
282     inode->size   = *(uint32_t *)root->size;
283     *inode->data  = *(uint32_t *)root->extent;
284     inode->blocks = (inode->size + BLOCK_SIZE(this_fs) - 1)
285         >> this_fs->block_shift;
286     
287     return inode;
288 }       
289
290 static struct inode *iso_iget(char *dname, struct inode *parent)
291 {
292     struct iso_dir_entry *de;
293     
294     de = iso_find_entry(dname, parent);
295     if (!de)
296         return NULL;
297     
298     return iso_get_inode(de);
299 }
300
301 /* Load the config file, return 1 if failed, or 0 */
302 static int iso_load_config(void)
303 {
304     const char *config_file[] = {
305         "/boot/isolinux/isolinux.cfg", 
306         "/isolinux/isolinux.cfg"
307     };
308     com32sys_t regs;
309     int i = 0;
310     char *p;
311     
312     for (; i < 2; i++) {
313             memset(&regs, 0, sizeof regs);
314             strcpy(ConfigName, config_file[i]);
315             regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
316             call16(core_open, &regs, &regs);
317             if (!(regs.eflags.l & EFLAGS_ZF))
318                 break;
319     }
320     if (i == 2) {
321         printf("No config file found\n");
322         return 1;
323     }
324     
325     strcpy(ConfigName, "isolinux.cfg");
326     strcpy(CurrentDirName, config_file[i]);
327     p = strrchr(CurrentDirName, '/');
328     *p = '\0';
329     
330     return 0;
331 }
332
333
334 static int iso_fs_init(struct fs_info *fs)
335 {
336     struct iso_sb_info *sbi;
337     
338     this_fs = fs;    
339         
340     sbi = malloc(sizeof(*sbi));
341     if (!sbi) {
342         malloc_error("iso_sb_info structure");
343         return 1;
344     }
345     fs->fs_info = sbi;
346     
347     cdrom_read_blocks(fs->fs_dev->disk, trackbuf, 16, 1);
348     memcpy(&sbi->root, trackbuf + ROOT_DIR_OFFSET, sizeof(sbi->root));
349     
350     fs->block_shift = 11;
351     return fs->block_shift;
352 }
353
354
355 const struct fs_ops iso_fs_ops = {
356     .fs_name       = "iso",
357     .fs_flags      = FS_USEMEM | FS_THISIND,
358     .fs_init       = iso_fs_init,
359     .searchdir     = NULL, 
360     .getfssec      = iso_getfssec,
361     .close_file    = iso_close_file,
362     .mangle_name   = iso_mangle_name,
363     .unmangle_name = generic_unmangle_name,
364     .load_config   = iso_load_config,
365     .iget_root     = iso_iget_root,
366     .iget_current  = NULL,
367     .iget          = iso_iget,    
368 };