xref: /minix3/minix/fs/isofs/inode.c (revision 03ac74ede908465cc64c671bbd209e761dc765dc)
1 
2 /*
3  * This file contains all the function that handle the dir records
4  * (inodes) for the ISO9660 filesystem.
5  */
6 
7 #include "inc.h"
8 
9 #include "uthash.h"
10 
11 struct inode_cache {
12 	ino_t key;
13 	struct inode *value;
14 	UT_hash_handle hh;
15 } ;
16 
17 struct inode_cache *icache = NULL;
18 
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);
22 
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
28 
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
34 
35 static int check_dir_record(const struct iso9660_dir_record *d, size_t offset);
36 
37 int fs_putnode(ino_t ino_nr, unsigned int count)
38 {
39        /*
40         * Find the inode specified by the request message and decrease its
41         * counter.
42         */
43 	struct inode *i_node;
44 
45 	if ((i_node = get_inode(ino_nr)) == NULL) {
46 		puts("ISOFS: trying to free unused inode");
47 		return EINVAL;
48 	}
49 	if (count > i_node->i_count) {
50 		puts("ISOFS: put_node count too high");
51 		return EINVAL;
52 	}
53 
54 	i_node->i_count -= count - 1;
55 	put_inode(i_node);
56 	return OK;
57 }
58 
59 
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);
63 
64 	if (i_node == NULL)
65 		return NULL;
66 
67 	if (i_node->i_count == 0)
68 		return NULL;
69 
70 	return i_node;
71 }
72 
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;
78 
79 	i_node->i_count++;
80 
81 	return i_node;
82 }
83 
84 void put_inode(struct inode *i_node) {
85 	if (i_node == NULL)
86 		return;
87 
88 	assert(i_node->i_count > 0);
89 	i_node->i_count--;
90 
91 	if(i_node->i_count == 0)
92 		i_node->i_mountpoint = FALSE;
93 }
94 
95 void dup_inode(struct inode *i_node) {
96 	assert(i_node != NULL);
97 	assert(i_node->i_count > 0);
98 
99 	i_node->i_count++;
100 }
101 
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;
108 
109 	if (dir->dir_contents)
110 		return OK;
111 
112 	if (!S_ISDIR(dir->i_stat.st_mode))
113 		return ENOTDIR;
114 
115 	/*
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.
122 	 *
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.
127 	 */
128 	cur_entry = 0;
129 	num_entries = 0;
130 
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;
137 		}
138 
139 		if (cur_entry < MAX_ENTRIES) {
140 			cur_entry++;
141 
142 			/*
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.
148 			 */
149 			saved_pos = pos;
150 		} else {
151 			/*
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.
155 			 */
156 			free_inode_dir_entry(&entries[cur_entry]);
157 		}
158 
159 		num_entries++;
160 	}
161 
162 	/*
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.
167 	 */
168 	dir->dir_contents =
169 	    alloc_mem(sizeof(struct inode_dir_entry) * num_entries);
170 
171 	memcpy(dir->dir_contents, entries,
172 	    sizeof(struct inode_dir_entry) * cur_entry);
173 
174 	/*
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.
179 	 */
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");
184 
185 		if (dir->dir_contents[cur_entry].i_node->skip) {
186 			free_inode_dir_entry(&entries[cur_entry]);
187 			continue;
188 		}
189 
190 		cur_entry++;
191 	}
192 
193 	dir->dir_size = num_entries;
194 
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;
203 	}
204 
205 	return (status == EOF) ? OK : status;
206 }
207 
208 int check_inodes(void) {
209 	/* Check whether there are no more inodes in use. Called on unmount. */
210 	int i;
211 
212 	/* XXX: actually check for inodes in use. */
213 	return TRUE;
214 }
215 
216 int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent,
217 	size_t *offset)
218 {
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;
224 
225 	/* Find inode. */
226 	bp = read_extent_block(extent, *offset);
227 	if (bp == NULL) {
228 		return EOF;
229 	}
230 
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);
234 
235 	if (dir_rec->length == 0) {
236 		*offset = ((*offset / v_pri.logical_block_size_l) + 1) *
237 		    v_pri.logical_block_size_l;
238 
239 		lmfs_put_block(bp);
240 		bp = read_extent_block(extent, *offset);
241 		if (bp == NULL) {
242 			return EOF;
243 		}
244 
245 		dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset %
246 	          v_pri.logical_block_size_l);
247 	}
248 
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;
254 	}
255 
256 	/* Get inode */
257 	if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) {
258 		ino_nr = dir_rec->loc_extent_l;
259 	}
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;
264 	}
265 
266 	memset(dir_entry, 0, sizeof(*dir_entry));
267 
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++;
273 
274 		memset(&dir_entry->i_name[0], 0, sizeof(dir_entry->i_name));
275 
276 		name_only = TRUE;
277 	}
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);
285 	}
286 
287 	dir_entry->i_node = i_node;
288 	read_inode_iso9660(dir_entry, dir_rec, extent, *offset, name_only);
289 
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
295 
296 	*offset += dir_rec->length;
297 	if (dir_rec->length % 2)
298 		(*offset)++;
299 
300 #ifdef ISO9660_OPTION_MODE3
301 	read_inode_extents(dir_entry, dir_rec, extent, offset);
302 #endif
303 
304 	lmfs_put_block(bp);
305 
306 	return OK;
307 }
308 
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);
312 
313 	if (i_node)
314 		return i_node->value;
315 	else
316 		return NULL;
317 }
318 
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;
322 
323 	HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), c_check);
324 
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;
329 
330 		HASH_ADD(hh, icache, key, sizeof(ino_t), c_entry);
331 	}
332 	else
333 		panic("Trying to insert inode into cache twice");
334 }
335 
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)
339 {
340 	char *cp;
341 
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);
349 
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';
357 		}
358 	}
359 
360 	if (name_only == TRUE)
361 		return;
362 
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;
369 
370 		if (dir_rec->data_length_l % v_pri.logical_block_size_l)
371 			i->i_node->extent.length++;
372 
373 		i->i_node->i_stat.st_size = dir_rec->data_length_l;
374 	}
375 
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);
380 
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;
385 
386 	i->i_node->i_stat.st_mode |= 0555;
387 
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;
393 }
394 
395 #ifdef ISO9660_OPTION_ROCKRIDGE
396 
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)
400 {
401 	int susp_offset, susp_size, name_length;
402 	struct rrii_dir_record rrii_data;
403 
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++;
408 	}
409 
410 	if(dir_rec->length - susp_offset < 4)
411 		return;
412 
413 	susp_size = dir_rec->length - susp_offset;
414 
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);
420 
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;
428 
429 	parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, susp_size);
430 
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);
436 	}
437 
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);
442 	}
443 
444 	if (rrii_data.reparented_inode) {
445 		/* Recycle the inode already parsed. */
446 		i->i_node = rrii_data.reparented_inode;
447 		return;
448 	}
449 
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;
453 
454 	if (name_only == TRUE)
455 		return;
456 
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);
462 
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;
467 }
468 
469 #endif
470 
471 #ifdef ISO9660_OPTION_MODE3
472 
473 void read_inode_extents(struct inode *i,
474 	const struct iso9660_dir_record *dir_rec,
475 	struct dir_extent *extent, size_t *offset)
476 {
477 	panic("read_inode_extents() isn't implemented yet!");
478 }
479 
480 #endif
481 
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;
490 
491 	return OK;
492 }
493