4 #include <sys/dirent.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')
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
)
30 static size_t iso_convert_name(char *dst
, const char *src
, int len
)
49 while (len
-- && (c
= *src
++)) {
50 if (c
== ';') /* Remove any filename version suffix */
52 *p
++ = iso_tolower(c
);
55 /* Then remove any terminal dots */
56 while (p
> dst
+1 && p
[-1] == '.')
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
;
75 i
= iso_convert_name(iso_file_name
, de_name
, len
);
77 dprintf("Compare: \"%s\" to \"%s\" (len %zu)\n",
78 file_name
, iso_file_name
, i
);
82 c2
= iso_tolower(*file_name
++);
84 /* compare equal except for case? */
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;
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
);
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
++);
117 de
= (const struct iso_dir_entry
*)(data
+ offset
);
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.
133 de_name_len
= de
->name_len
;
135 if (iso_compare_name(de_name
, de_name_len
, dname
)) {
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
);
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
;
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
);
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
;
198 size_t offset
= file
->offset
& (BLOCK_SIZE(fs
) - 1);
201 uint32_t i
= file
->offset
>> BLOCK_SHIFT(fs
);
202 if (i
>= inode
->blocks
)
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 */
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 */
228 /* Load the config file, return 1 if failed, or 0 */
229 static int iso_load_config(void)
231 static const char *search_directories
[] = {
239 static const char *filenames
[] = {
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 */
253 struct disk
*disk
= fs
->fs_dev
->disk
;
256 sbi
= malloc(sizeof(*sbi
));
258 malloc_error("iso_sb_info structure");
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
;
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
= {
290 .fs_flags
= FS_USEMEM
| FS_THISIND
,
291 .fs_init
= iso_fs_init
,
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
,
299 .readdir
= iso_readdir
,
300 .next_extent
= no_next_extent
,