xref: /minix/minix/fs/isofs/inode.c (revision 0a6a1f1d)
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 4096
104 	/* Read all entries in a directory. */
105 	size_t pos = 0, cur_entry = 0, cpt;
106 	struct inode_dir_entry entries[MAX_ENTRIES];
107 	int status = OK;
108 
109 	if (dir->dir_contents)
110 		return OK;
111 
112 	if (!S_ISDIR(dir->i_stat.st_mode))
113 		return ENOTDIR;
114 
115 	for (cur_entry = 0; status == OK && cur_entry < MAX_ENTRIES; cur_entry++) {
116 		memset(&entries[cur_entry], 0, sizeof(struct inode_dir_entry));
117 
118 		status = read_inode(&entries[cur_entry], &dir->extent, &pos);
119 		if (status != OK)
120 			break;
121 
122 		/* Dump the entry if it's not to be exported to userland. */
123 		if (entries[cur_entry].i_node->skip) {
124 			free_inode_dir_entry(&entries[cur_entry]);
125 			continue;
126 		}
127 	}
128 
129 	/* Resize dynamic array to correct size */
130 	dir->dir_contents = alloc_mem(sizeof(struct inode_dir_entry) * cur_entry);
131 	memcpy(dir->dir_contents, entries, sizeof(struct inode_dir_entry) * cur_entry);
132 	dir->dir_size = cur_entry;
133 
134 	/* The name pointer has to point to the new memory location. */
135 	for (cpt = 0; cpt < cur_entry; cpt++) {
136 		if (dir->dir_contents[cpt].r_name == NULL)
137 			dir->dir_contents[cpt].name =
138 			    dir->dir_contents[cpt].i_name;
139 		else
140 			dir->dir_contents[cpt].name =
141 			    dir->dir_contents[cpt].r_name;
142 	}
143 
144 	return (status == EOF) ? OK : status;
145 }
146 
147 int check_inodes(void) {
148 	/* Check whether there are no more inodes in use. Called on unmount. */
149 	int i;
150 
151 	/* XXX: actually check for inodes in use. */
152 	return TRUE;
153 }
154 
155 int read_inode(struct inode_dir_entry *dir_entry, struct dir_extent *extent,
156 	size_t *offset)
157 {
158 	struct iso9660_dir_record *dir_rec;
159 	struct buf *bp;
160 	struct inode *i_node;
161 	ino_t ino_nr;
162 	int name_only = FALSE;
163 
164 	/* Find inode. */
165 	bp = read_extent_block(extent, *offset);
166 	if (bp == NULL) {
167 		return EOF;
168 	}
169 
170 	/* Check if we are crossing a sector boundary. */
171 	dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset %
172 	          v_pri.logical_block_size_l);
173 
174 	if (dir_rec->length == 0) {
175 		*offset = ((*offset / v_pri.logical_block_size_l) + 1) *
176 		    v_pri.logical_block_size_l;
177 
178 		lmfs_put_block(bp);
179 		bp = read_extent_block(extent, *offset);
180 		if (bp == NULL) {
181 			return EOF;
182 		}
183 
184 		dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset %
185 	          v_pri.logical_block_size_l);
186 	}
187 
188 	/* Parse basic ISO 9660 specs. */
189 	if (check_dir_record(dir_rec, *offset % v_pri.logical_block_size_l)
190 	    != OK) {
191 		lmfs_put_block(bp);
192 		return EINVAL;
193 	}
194 
195 	/* Get inode */
196 	if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) {
197 		ino_nr = dir_rec->loc_extent_l;
198 	}
199 	else {
200 		ino_nr = get_extent_absolute_block_id(extent, *offset)
201 		    * v_pri.logical_block_size_l +
202 		    *offset % v_pri.logical_block_size_l;
203 	}
204 
205 	i_node = inode_cache_get(ino_nr);
206 	if (i_node) {
207 		/* Inode was already loaded, parse file names only. */
208 		dir_entry->i_node = i_node;
209 		i_node->i_refcount++;
210 
211 		memset(&dir_entry->i_name[0], 0, sizeof(dir_entry->i_name));
212 
213 		name_only = TRUE;
214 	}
215 	else {
216 		/* Inode wasn't in memory, parse it. */
217 		i_node = alloc_mem(sizeof(struct inode));
218 		dir_entry->i_node = i_node;
219 		i_node->i_refcount = 1;
220 		i_node->i_stat.st_ino = ino_nr;
221 		inode_cache_add(ino_nr, i_node);
222 	}
223 
224 	dir_entry->i_node = i_node;
225 	read_inode_iso9660(dir_entry, dir_rec, extent, *offset, name_only);
226 
227 	/* Parse extensions. */
228 #ifdef ISO9660_OPTION_ROCKRIDGE
229 	read_inode_susp(dir_entry, dir_rec, bp,
230 	    *offset % v_pri.logical_block_size_l, name_only);
231 #endif
232 
233 	*offset += dir_rec->length;
234 	if (dir_rec->length % 2)
235 		(*offset)++;
236 
237 #ifdef ISO9660_OPTION_MODE3
238 	read_inode_extents(dir_entry, dir_rec, extent, offset);
239 #endif
240 
241 	lmfs_put_block(bp);
242 
243 	return OK;
244 }
245 
246 struct inode* inode_cache_get(ino_t ino_nr) {
247 	struct inode_cache *i_node;
248 	HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), i_node);
249 
250 	if (i_node)
251 		return i_node->value;
252 	else
253 		return NULL;
254 }
255 
256 void inode_cache_add(ino_t ino_nr, struct inode *i_node) {
257 	struct inode_cache *c_check;
258 	struct inode_cache *c_entry;
259 
260 	HASH_FIND(hh, icache, &ino_nr, sizeof(ino_t), c_check);
261 
262 	if (c_check == NULL) {
263 		c_entry = alloc_mem(sizeof(struct inode_cache));
264 		c_entry->key = ino_nr;
265 		c_entry->value = i_node;
266 
267 		HASH_ADD(hh, icache, key, sizeof(ino_t), c_entry);
268 	}
269 	else
270 		panic("Trying to insert inode into cache twice");
271 }
272 
273 void read_inode_iso9660(struct inode_dir_entry *i,
274 	const struct iso9660_dir_record *dir_rec, struct dir_extent *extent,
275 	size_t offset, int name_only)
276 {
277 	char *cp;
278 
279 	/* Parse file name. */
280 	if (dir_rec->file_id[0] == 0)
281 		strcpy(i->i_name, ".");
282 	else if (dir_rec->file_id[0] == 1)
283 		strcpy(i->i_name, "..");
284 	else {
285 		memcpy(i->i_name, dir_rec->file_id, dir_rec->length_file_id);
286 
287 		/* Truncate/ignore file version suffix. */
288 		cp = strchr(i->i_name, ';');
289 		if (cp != NULL) {
290 			*cp = '\0';
291 			/* Truncate dot if file has no extension. */
292 			if (strchr(i->i_name, '.') + 1 == cp)
293 				*(cp-1) = '\0';
294 		}
295 	}
296 
297 	if (name_only == TRUE)
298 		return;
299 
300 	/* Parse first extent. */
301 	if (dir_rec->data_length_l > 0) {
302 		i->i_node->extent.location = dir_rec->loc_extent_l +
303 		    dir_rec->ext_attr_rec_length;
304 		i->i_node->extent.length = dir_rec->data_length_l /
305 		    v_pri.logical_block_size_l;
306 
307 		if (dir_rec->data_length_l % v_pri.logical_block_size_l)
308 			i->i_node->extent.length++;
309 
310 		i->i_node->i_stat.st_size = dir_rec->data_length_l;
311 	}
312 
313 	/* Parse timestamps (record date). */
314 	i->i_node->i_stat.st_atime = i->i_node->i_stat.st_mtime =
315 	    i->i_node->i_stat.st_ctime = i->i_node->i_stat.st_birthtime =
316 	    date7_to_time_t(dir_rec->rec_date);
317 
318 	if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY)
319 		i->i_node->i_stat.st_mode = S_IFDIR;
320 	else
321 		i->i_node->i_stat.st_mode = S_IFREG;
322 
323 	i->i_node->i_stat.st_mode |= 0555;
324 
325 	/* Initialize stat. */
326 	i->i_node->i_stat.st_dev = fs_dev;
327 	i->i_node->i_stat.st_blksize = v_pri.logical_block_size_l;
328 	i->i_node->i_stat.st_blocks =
329 	    dir_rec->data_length_l / v_pri.logical_block_size_l;
330 	i->i_node->i_stat.st_nlink = 1;
331 }
332 
333 #ifdef ISO9660_OPTION_ROCKRIDGE
334 
335 void read_inode_susp(struct inode_dir_entry *i,
336 	const struct iso9660_dir_record *dir_rec, struct buf *bp, size_t offset,
337 	int name_only)
338 {
339 	int susp_offset, susp_size, name_length;
340 	struct rrii_dir_record rrii_data;
341 
342 	susp_offset = 33 + dir_rec->length_file_id;
343 	/* Get rid of padding byte. */
344 	if(dir_rec->length_file_id % 2 == 0) {
345 		susp_offset++;
346 	}
347 
348 	if(dir_rec->length - susp_offset < 4)
349 		return;
350 
351 	susp_size = dir_rec->length - susp_offset;
352 
353 	/* Initialize record with known, sane data. */
354 	memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
355 	memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
356 	memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
357 	memcpy(rrii_data.birthtime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
358 
359 	rrii_data.d_mode = i->i_node->i_stat.st_mode;
360 	rrii_data.uid    = SYS_UID;
361 	rrii_data.gid    = SYS_GID;
362 	rrii_data.rdev   = NO_DEV;
363 	rrii_data.file_id_rrip[0] = '\0';
364 	rrii_data.slink_rrip[0]   = '\0';
365 	rrii_data.reparented_inode = NULL;
366 
367 	parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset, susp_size);
368 
369 	/* Copy back data from rrii_dir_record structure. */
370 	if (rrii_data.file_id_rrip[0] != '\0') {
371 		name_length = strlen(rrii_data.file_id_rrip);
372 		i->r_name = alloc_mem(name_length + 1);
373 		memcpy(i->r_name, rrii_data.file_id_rrip, name_length);
374 	}
375 
376 	if (rrii_data.slink_rrip[0] != '\0') {
377 		name_length = strlen(rrii_data.slink_rrip);
378 		i->i_node->s_name = alloc_mem(name_length + 1);
379 		memcpy(i->i_node->s_name, rrii_data.slink_rrip, name_length);
380 	}
381 
382 	if (rrii_data.reparented_inode) {
383 		/* Recycle the inode already parsed. */
384 		i->i_node = rrii_data.reparented_inode;
385 		return;
386 	}
387 
388 	/* XXX: not the correct way to ignore reparented directory holder... */
389 	if (strcmp(rrii_data.file_id_rrip, ".rr_moved") == 0)
390 		i->i_node->skip = 1;
391 
392 	if (name_only == TRUE)
393 		return;
394 
395 	/* Write back all Rock Ridge properties. */
396 	i->i_node->i_stat.st_atime = date7_to_time_t(rrii_data.atime);
397 	i->i_node->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime);
398 	i->i_node->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime);
399 	i->i_node->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime);
400 
401 	i->i_node->i_stat.st_mode = rrii_data.d_mode;
402 	i->i_node->i_stat.st_uid  = rrii_data.uid;
403 	i->i_node->i_stat.st_gid  = rrii_data.gid;
404 	i->i_node->i_stat.st_rdev = rrii_data.rdev;
405 }
406 
407 #endif
408 
409 #ifdef ISO9660_OPTION_MODE3
410 
411 void read_inode_extents(struct inode *i,
412 	const struct iso9660_dir_record *dir_rec,
413 	struct dir_extent *extent, size_t *offset)
414 {
415 	panic("read_inode_extents() isn't implemented yet!");
416 }
417 
418 #endif
419 
420 int check_dir_record(const struct iso9660_dir_record *d, size_t offset) {
421 	/* Run some consistency check on a directory entry. */
422 	if ((d->length < 33) || (d->length_file_id < 1))
423 		return EINVAL;
424 	if (d->length_file_id + 32 > d->length)
425 		return EINVAL;
426 	if (offset + d->length > v_pri.logical_block_size_l)
427 		return EINVAL;
428 
429 	return OK;
430 }
431