3 * This file contains all the function that handle the dir records
4 * (inodes) for the ISO9660 filesystem.
17 struct inode_cache
*icache
= NULL
;
19 void read_inode_iso9660(struct inode_dir_entry
*i
,
20 const struct iso9660_dir_record
*dir_rec
, struct dir_extent
*extent
,
21 size_t offset
, int name_only
);
23 #ifdef ISO9660_OPTION_MODE3
24 static void read_inode_extents(struct inode_dir_entry
*i
,
25 const struct iso9660_dir_record
*dir_rec
, struct dir_extent
*extent
,
29 #ifdef ISO9660_OPTION_ROCKRIDGE
30 void read_inode_susp(struct inode_dir_entry
*i
,
31 const struct iso9660_dir_record
*dir_rec
, struct buf
*bp
, size_t offset
,
35 static int check_dir_record(const struct iso9660_dir_record
*d
, size_t offset
);
37 int fs_putnode(ino_t ino_nr
, unsigned int count
)
40 * Find the inode specified by the request message and decrease its
45 if ((i_node
= get_inode(ino_nr
)) == NULL
) {
46 puts("ISOFS: trying to free unused inode");
49 if (count
> i_node
->i_count
) {
50 puts("ISOFS: put_node count too high");
54 i_node
->i_count
-= count
- 1;
60 struct inode
* get_inode(ino_t ino_nr
) {
61 /* Return an already opened inode from cache. */
62 struct inode
*i_node
= inode_cache_get(ino_nr
);
67 if (i_node
->i_count
== 0)
73 struct inode
* open_inode(ino_t ino_nr
) {
74 /* Return an inode from cache. */
75 struct inode
*i_node
= inode_cache_get(ino_nr
);
84 void put_inode(struct inode
*i_node
) {
88 assert(i_node
->i_count
> 0);
91 if(i_node
->i_count
== 0)
92 i_node
->i_mountpoint
= FALSE
;
95 void dup_inode(struct inode
*i_node
) {
96 assert(i_node
!= NULL
);
97 assert(i_node
->i_count
> 0);
102 int read_directory(struct inode
*dir
) {
103 #define MAX_ENTRIES 256 /* avoid using lots of stack.. */
104 /* Read all entries in a directory. */
105 size_t pos
= 0, saved_pos
, cur_entry
, num_entries
, cpt
;
106 struct inode_dir_entry entries
[MAX_ENTRIES
+ 1];
109 if (dir
->dir_contents
)
112 if (!S_ISDIR(dir
->i_stat
.st_mode
))
116 * We do not know how many inode entries we will find, but we want to
117 * allocate an array of the right size for dir->dir_contents. First
118 * find out how many entries there are, and store up to MAX_ENTRIES of
119 * them into a temporary array on the stack. If there are more than
120 * MAX_ENTRIES entries, we have to do a second pass on the part of the
121 * directory that we did not manage to fit in the temporary array.
123 * The entire service needs massive structural improvement (and in
124 * particular, no dynamic memory allocation like this), but for now
125 * this is the simplest way to be fast for small directories while at
126 * the same time supporting seriously large directories.
131 while ((status
= read_inode(&entries
[cur_entry
], &dir
->extent
,
133 /* Dump the entry if it's not to be exported to userland. */
134 if (entries
[cur_entry
].i_node
->skip
) {
135 free_inode_dir_entry(&entries
[cur_entry
]);
139 if (cur_entry
< MAX_ENTRIES
) {
143 * As long as more entries fit in the temporary array,
144 * update the saved position of the next entry. Once
145 * we hit the first entry that does not fit (if any),
146 * the updating stops and we will have the correct
152 * No room in the temporary array. Free the entry
153 * again. This is costly but only for those rare
154 * directories that have more than MAX_ENTRIES entries.
156 free_inode_dir_entry(&entries
[cur_entry
]);
163 * Allocate a dynamic array of the correct size, and populate it with
164 * all the entries in the temporary array. For large directories, the
165 * temporary array will have partial results, in which case we have to
166 * do a second pass on the rest below.
169 alloc_mem(sizeof(struct inode_dir_entry
) * num_entries
);
171 memcpy(dir
->dir_contents
, entries
,
172 sizeof(struct inode_dir_entry
) * cur_entry
);
175 * The second pass. This pass starts from the saved position and reads
176 * only the entries that did not fit in the temporary array. This time
177 * we can read straight into the actual destination array. We expect
178 * to find the same entries as during the first pass.
180 while (cur_entry
< num_entries
) {
181 if (read_inode(&dir
->dir_contents
[cur_entry
], &dir
->extent
,
183 panic("unexpected EOF or error rereading directory");
185 if (dir
->dir_contents
[cur_entry
].i_node
->skip
) {
186 free_inode_dir_entry(&entries
[cur_entry
]);
193 dir
->dir_size
= num_entries
;
195 /* The name pointer has to point to the new memory location. */
196 for (cpt
= 0; cpt
< num_entries
; cpt
++) {
197 if (dir
->dir_contents
[cpt
].r_name
== NULL
)
198 dir
->dir_contents
[cpt
].name
=
199 dir
->dir_contents
[cpt
].i_name
;
201 dir
->dir_contents
[cpt
].name
=
202 dir
->dir_contents
[cpt
].r_name
;
205 return (status
== EOF
) ? OK
: status
;
208 int check_inodes(void) {
209 /* Check whether there are no more inodes in use. Called on unmount. */
212 /* XXX: actually check for inodes in use. */
216 int read_inode(struct inode_dir_entry
*dir_entry
, struct dir_extent
*extent
,
219 struct iso9660_dir_record
*dir_rec
;
221 struct inode
*i_node
;
223 int name_only
= FALSE
;
226 bp
= read_extent_block(extent
, *offset
);
231 /* Check if we are crossing a sector boundary. */
232 dir_rec
= (struct iso9660_dir_record
*)(b_data(bp
) + *offset
%
233 v_pri
.logical_block_size_l
);
235 if (dir_rec
->length
== 0) {
236 *offset
= ((*offset
/ v_pri
.logical_block_size_l
) + 1) *
237 v_pri
.logical_block_size_l
;
240 bp
= read_extent_block(extent
, *offset
);
245 dir_rec
= (struct iso9660_dir_record
*)(b_data(bp
) + *offset
%
246 v_pri
.logical_block_size_l
);
249 /* Parse basic ISO 9660 specs. */
250 if (check_dir_record(dir_rec
, *offset
% v_pri
.logical_block_size_l
)
257 if ((dir_rec
->file_flags
& D_TYPE
) == D_DIRECTORY
) {
258 ino_nr
= dir_rec
->loc_extent_l
;
261 ino_nr
= get_extent_absolute_block_id(extent
, *offset
)
262 * v_pri
.logical_block_size_l
+
263 *offset
% v_pri
.logical_block_size_l
;
266 memset(dir_entry
, 0, sizeof(*dir_entry
));
268 i_node
= inode_cache_get(ino_nr
);
270 /* Inode was already loaded, parse file names only. */
271 dir_entry
->i_node
= i_node
;
272 i_node
->i_refcount
++;
274 memset(&dir_entry
->i_name
[0], 0, sizeof(dir_entry
->i_name
));
279 /* Inode wasn't in memory, parse it. */
280 i_node
= alloc_mem(sizeof(struct inode
));
281 dir_entry
->i_node
= i_node
;
282 i_node
->i_refcount
= 1;
283 i_node
->i_stat
.st_ino
= ino_nr
;
284 inode_cache_add(ino_nr
, i_node
);
287 dir_entry
->i_node
= i_node
;
288 read_inode_iso9660(dir_entry
, dir_rec
, extent
, *offset
, name_only
);
290 /* Parse extensions. */
291 #ifdef ISO9660_OPTION_ROCKRIDGE
292 read_inode_susp(dir_entry
, dir_rec
, bp
,
293 *offset
% v_pri
.logical_block_size_l
, name_only
);
296 *offset
+= dir_rec
->length
;
297 if (dir_rec
->length
% 2)
300 #ifdef ISO9660_OPTION_MODE3
301 read_inode_extents(dir_entry
, dir_rec
, extent
, offset
);
309 struct inode
* inode_cache_get(ino_t ino_nr
) {
310 struct inode_cache
*i_node
;
311 HASH_FIND(hh
, icache
, &ino_nr
, sizeof(ino_t
), i_node
);
314 return i_node
->value
;
319 void inode_cache_add(ino_t ino_nr
, struct inode
*i_node
) {
320 struct inode_cache
*c_check
;
321 struct inode_cache
*c_entry
;
323 HASH_FIND(hh
, icache
, &ino_nr
, sizeof(ino_t
), c_check
);
325 if (c_check
== NULL
) {
326 c_entry
= alloc_mem(sizeof(struct inode_cache
));
327 c_entry
->key
= ino_nr
;
328 c_entry
->value
= i_node
;
330 HASH_ADD(hh
, icache
, key
, sizeof(ino_t
), c_entry
);
333 panic("Trying to insert inode into cache twice");
336 void read_inode_iso9660(struct inode_dir_entry
*i
,
337 const struct iso9660_dir_record
*dir_rec
, struct dir_extent
*extent
,
338 size_t offset
, int name_only
)
342 /* Parse file name. */
343 if (dir_rec
->file_id
[0] == 0)
344 strcpy(i
->i_name
, ".");
345 else if (dir_rec
->file_id
[0] == 1)
346 strcpy(i
->i_name
, "..");
348 memcpy(i
->i_name
, dir_rec
->file_id
, dir_rec
->length_file_id
);
350 /* Truncate/ignore file version suffix. */
351 cp
= strchr(i
->i_name
, ';');
354 /* Truncate dot if file has no extension. */
355 if (strchr(i
->i_name
, '.') + 1 == cp
)
360 if (name_only
== TRUE
)
363 /* Parse first extent. */
364 if (dir_rec
->data_length_l
> 0) {
365 i
->i_node
->extent
.location
= dir_rec
->loc_extent_l
+
366 dir_rec
->ext_attr_rec_length
;
367 i
->i_node
->extent
.length
= dir_rec
->data_length_l
/
368 v_pri
.logical_block_size_l
;
370 if (dir_rec
->data_length_l
% v_pri
.logical_block_size_l
)
371 i
->i_node
->extent
.length
++;
373 i
->i_node
->i_stat
.st_size
= dir_rec
->data_length_l
;
376 /* Parse timestamps (record date). */
377 i
->i_node
->i_stat
.st_atime
= i
->i_node
->i_stat
.st_mtime
=
378 i
->i_node
->i_stat
.st_ctime
= i
->i_node
->i_stat
.st_birthtime
=
379 date7_to_time_t(dir_rec
->rec_date
);
381 if ((dir_rec
->file_flags
& D_TYPE
) == D_DIRECTORY
)
382 i
->i_node
->i_stat
.st_mode
= S_IFDIR
;
384 i
->i_node
->i_stat
.st_mode
= S_IFREG
;
386 i
->i_node
->i_stat
.st_mode
|= 0555;
388 /* Initialize stat. */
389 i
->i_node
->i_stat
.st_dev
= fs_dev
;
390 i
->i_node
->i_stat
.st_blksize
= v_pri
.logical_block_size_l
;
391 i
->i_node
->i_stat
.st_blocks
= dir_rec
->data_length_l
/ 512;
392 i
->i_node
->i_stat
.st_nlink
= 1;
395 #ifdef ISO9660_OPTION_ROCKRIDGE
397 void read_inode_susp(struct inode_dir_entry
*i
,
398 const struct iso9660_dir_record
*dir_rec
, struct buf
*bp
, size_t offset
,
401 int susp_offset
, susp_size
, name_length
;
402 struct rrii_dir_record rrii_data
;
404 susp_offset
= 33 + dir_rec
->length_file_id
;
405 /* Get rid of padding byte. */
406 if(dir_rec
->length_file_id
% 2 == 0) {
410 if(dir_rec
->length
- susp_offset
< 4)
413 susp_size
= dir_rec
->length
- susp_offset
;
415 /* Initialize record with known, sane data. */
416 memcpy(rrii_data
.mtime
, dir_rec
->rec_date
, ISO9660_SIZE_DATE7
);
417 memcpy(rrii_data
.atime
, dir_rec
->rec_date
, ISO9660_SIZE_DATE7
);
418 memcpy(rrii_data
.ctime
, dir_rec
->rec_date
, ISO9660_SIZE_DATE7
);
419 memcpy(rrii_data
.birthtime
, dir_rec
->rec_date
, ISO9660_SIZE_DATE7
);
421 rrii_data
.d_mode
= i
->i_node
->i_stat
.st_mode
;
422 rrii_data
.uid
= SYS_UID
;
423 rrii_data
.gid
= SYS_GID
;
424 rrii_data
.rdev
= NO_DEV
;
425 rrii_data
.file_id_rrip
[0] = '\0';
426 rrii_data
.slink_rrip
[0] = '\0';
427 rrii_data
.reparented_inode
= NULL
;
429 parse_susp_buffer(&rrii_data
, b_data(bp
)+offset
+susp_offset
, susp_size
);
431 /* Copy back data from rrii_dir_record structure. */
432 if (rrii_data
.file_id_rrip
[0] != '\0') {
433 name_length
= strlen(rrii_data
.file_id_rrip
);
434 i
->r_name
= alloc_mem(name_length
+ 1);
435 memcpy(i
->r_name
, rrii_data
.file_id_rrip
, name_length
);
438 if (rrii_data
.slink_rrip
[0] != '\0') {
439 name_length
= strlen(rrii_data
.slink_rrip
);
440 i
->i_node
->s_name
= alloc_mem(name_length
+ 1);
441 memcpy(i
->i_node
->s_name
, rrii_data
.slink_rrip
, name_length
);
444 if (rrii_data
.reparented_inode
) {
445 /* Recycle the inode already parsed. */
446 i
->i_node
= rrii_data
.reparented_inode
;
450 /* XXX: not the correct way to ignore reparented directory holder... */
451 if (strcmp(rrii_data
.file_id_rrip
, ".rr_moved") == 0)
454 if (name_only
== TRUE
)
457 /* Write back all Rock Ridge properties. */
458 i
->i_node
->i_stat
.st_atime
= date7_to_time_t(rrii_data
.atime
);
459 i
->i_node
->i_stat
.st_ctime
= date7_to_time_t(rrii_data
.ctime
);
460 i
->i_node
->i_stat
.st_mtime
= date7_to_time_t(rrii_data
.mtime
);
461 i
->i_node
->i_stat
.st_birthtime
= date7_to_time_t(rrii_data
.birthtime
);
463 i
->i_node
->i_stat
.st_mode
= rrii_data
.d_mode
;
464 i
->i_node
->i_stat
.st_uid
= rrii_data
.uid
;
465 i
->i_node
->i_stat
.st_gid
= rrii_data
.gid
;
466 i
->i_node
->i_stat
.st_rdev
= rrii_data
.rdev
;
471 #ifdef ISO9660_OPTION_MODE3
473 void read_inode_extents(struct inode
*i
,
474 const struct iso9660_dir_record
*dir_rec
,
475 struct dir_extent
*extent
, size_t *offset
)
477 panic("read_inode_extents() isn't implemented yet!");
482 int check_dir_record(const struct iso9660_dir_record
*d
, size_t offset
) {
483 /* Run some consistency check on a directory entry. */
484 if ((d
->length
< 33) || (d
->length_file_id
< 1))
486 if (d
->length_file_id
+ 32 > d
->length
)
488 if (offset
+ d
->length
> v_pri
.logical_block_size_l
)