Drop main() prototype. Syncs with NetBSD-8
[minix.git] / minix / fs / isofs / inode.c
blob4e8ac519e951e6478df5c05859f1b57e9d4b73c7
2 /*
3 * This file contains all the function that handle the dir records
4 * (inodes) for the ISO9660 filesystem.
5 */
7 #include "inc.h"
9 #include "uthash.h"
11 struct inode_cache {
12 ino_t key;
13 struct inode *value;
14 UT_hash_handle hh;
15 } ;
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,
26 size_t *offset);
27 #endif
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,
32 int name_only);
33 #endif
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
41 * counter.
43 struct inode *i_node;
45 if ((i_node = get_inode(ino_nr)) == NULL) {
46 puts("ISOFS: trying to free unused inode");
47 return EINVAL;
49 if (count > i_node->i_count) {
50 puts("ISOFS: put_node count too high");
51 return EINVAL;
54 i_node->i_count -= count - 1;
55 put_inode(i_node);
56 return OK;
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);
64 if (i_node == NULL)
65 return NULL;
67 if (i_node->i_count == 0)
68 return NULL;
70 return i_node;
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);
76 if (i_node == NULL)
77 return NULL;
79 i_node->i_count++;
81 return i_node;
84 void put_inode(struct inode *i_node) {
85 if (i_node == NULL)
86 return;
88 assert(i_node->i_count > 0);
89 i_node->i_count--;
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);
99 i_node->i_count++;
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];
107 int status;
109 if (dir->dir_contents)
110 return OK;
112 if (!S_ISDIR(dir->i_stat.st_mode))
113 return ENOTDIR;
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.
128 cur_entry = 0;
129 num_entries = 0;
131 while ((status = read_inode(&entries[cur_entry], &dir->extent,
132 &pos)) == OK) {
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]);
136 continue;
139 if (cur_entry < MAX_ENTRIES) {
140 cur_entry++;
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
147 * saved position.
149 saved_pos = pos;
150 } else {
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]);
159 num_entries++;
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.
168 dir->dir_contents =
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,
182 &saved_pos) != OK)
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]);
187 continue;
190 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;
200 else
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. */
210 int i;
212 /* XXX: actually check for inodes in use. */
213 return TRUE;
216 int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent,
217 size_t *offset)
219 struct iso9660_dir_record *dir_rec;
220 struct buf *bp;
221 struct inode *i_node;
222 ino_t ino_nr;
223 int name_only = FALSE;
225 /* Find inode. */
226 bp = read_extent_block(extent, *offset);
227 if (bp == NULL) {
228 return EOF;
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;
239 lmfs_put_block(bp);
240 bp = read_extent_block(extent, *offset);
241 if (bp == NULL) {
242 return EOF;
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)
251 != OK) {
252 lmfs_put_block(bp);
253 return EINVAL;
256 /* Get inode */
257 if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) {
258 ino_nr = dir_rec->loc_extent_l;
260 else {
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);
269 if (i_node) {
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));
276 name_only = TRUE;
278 else {
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);
294 #endif
296 *offset += dir_rec->length;
297 if (dir_rec->length % 2)
298 (*offset)++;
300 #ifdef ISO9660_OPTION_MODE3
301 read_inode_extents(dir_entry, dir_rec, extent, offset);
302 #endif
304 lmfs_put_block(bp);
306 return OK;
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);
313 if (i_node)
314 return i_node->value;
315 else
316 return NULL;
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);
332 else
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)
340 char *cp;
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, "..");
347 else {
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, ';');
352 if (cp != NULL) {
353 *cp = '\0';
354 /* Truncate dot if file has no extension. */
355 if (strchr(i->i_name, '.') + 1 == cp)
356 *(cp-1) = '\0';
360 if (name_only == TRUE)
361 return;
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;
383 else
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,
399 int name_only)
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) {
407 susp_offset++;
410 if(dir_rec->length - susp_offset < 4)
411 return;
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;
447 return;
450 /* XXX: not the correct way to ignore reparented directory holder... */
451 if (strcmp(rrii_data.file_id_rrip, ".rr_moved") == 0)
452 i->i_node->skip = 1;
454 if (name_only == TRUE)
455 return;
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;
469 #endif
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!");
480 #endif
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))
485 return EINVAL;
486 if (d->length_file_id + 32 > d->length)
487 return EINVAL;
488 if (offset + d->length > v_pri.logical_block_size_l)
489 return EINVAL;
491 return OK;