xref: /minix3/minix/fs/isofs/inode.c (revision b80da2a01d0bb632707b7b4e974aa32eaebbcc6f)
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 static struct inode inodes[NR_INODE_RECORDS];
10 static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset);
11 
12 int fs_putnode(ino_t ino_nr, unsigned int count)
13 {
14 	/*
15 	 * Find the inode specified by the request message and decrease its
16 	 * counter.
17 	 */
18 	struct inode *i_node;
19 
20 	if ((i_node = find_inode(ino_nr)) == NULL) {
21 		printf("ISOFS: trying to free unused inode\n");
22 		return EINVAL;
23 	}
24 	if (count > i_node->i_count) {
25 		printf("ISOFS: put_node count too high\n");
26 		return EINVAL;
27 	}
28 
29 	i_node->i_count -= count - 1;
30 	put_inode(i_node);
31 	return OK;
32 }
33 
34 struct inode* alloc_inode(void)
35 {
36 	/*
37 	 * Return a free inode from the pool.
38 	 */
39 	static int i;
40 	int end = i;
41 	struct inode *i_node;
42 
43 	i = (i + 1) % NR_INODE_RECORDS;
44 	do {
45 		i_node = &inodes[i];
46 
47 		if (i_node->i_count == 0) {
48 			free_extent(i_node->extent);
49 
50 			memset(i_node, 0, sizeof(*i_node));
51 			i_node->i_count = 1;
52 
53 			return i_node;
54 		}
55 
56 		i = (i + 1) % NR_INODE_RECORDS;
57 	}
58 	while(i != end);
59 
60 	panic("No free inodes in cache");
61 }
62 
63 struct inode* find_inode(ino_t i)
64 {
65 	/* Get inode from cache. */
66 	int cpt;
67 	struct inode *i_node;
68 
69 	if (i == 0)
70 		return NULL;
71 
72 	for (cpt = 0; cpt < NR_INODE_RECORDS; cpt++) {
73 		i_node = &inodes[cpt];
74 
75 		if ((i_node->i_stat.st_ino == i) && (i_node->i_count > 0))
76 			return i_node;
77 	}
78 
79 	return NULL;
80 }
81 
82 struct inode* get_inode(ino_t i)
83 {
84 	struct inode *i_node;
85 	struct dir_extent *extent;
86 
87 	if (i == 0)
88 		return NULL;
89 
90 	/* Try to get inode from cache. */
91 	i_node = find_inode(i);
92 	if (i_node != NULL) {
93 		dup_inode(i_node);
94 		return i_node;
95 	}
96 
97 	/*
98 	 * Inode wasn't in cache, try to load it.
99 	 * FIXME: a fake extent of one logical block is created for
100 	 * read_inode(). Reading a inode this way could be problematic if
101 	 * additional extents are stored behind the block boundary.
102 	 */
103 	i_node = alloc_inode();
104 	extent = alloc_extent();
105 	extent->location = i / v_pri.logical_block_size_l;
106 	extent->length = 1;
107 
108 	if (read_inode(i_node, extent, i % v_pri.logical_block_size_l,
109 	    NULL) != OK) {
110 		free_extent(extent);
111 		put_inode(i_node);
112 		return NULL;
113 	}
114 
115 	free_extent(extent);
116 	return i_node;
117 }
118 
119 void put_inode(struct inode *i_node)
120 {
121 	if (i_node == NULL)
122 		return;
123 
124 	assert(i_node->i_count > 0);
125 
126 	i_node->i_count--;
127 }
128 
129 void dup_inode(struct inode *i_node)
130 {
131 	assert(i_node != NULL);
132 
133 	i_node->i_count++;
134 }
135 
136 static struct buf* fetch_inode(struct dir_extent *extent, size_t *offset)
137 {
138 	struct iso9660_dir_record *dir_rec;
139 	struct buf *bp;
140 
141 	/*
142 	 * Directory entries aren't allowed to cross a logical block boundary in
143 	 * ISO 9660, so we keep searching until we find something or reach the
144 	 * end of the extent.
145 	 */
146 	bp = read_extent_block(extent, *offset / v_pri.logical_block_size_l);
147 	while (bp != NULL) {
148 		dir_rec = (struct iso9660_dir_record*)(b_data(bp) + *offset %
149 		          v_pri.logical_block_size_l);
150 		if (dir_rec->length == 0) {
151 			*offset -= *offset % v_pri.logical_block_size_l;
152 			*offset += v_pri.logical_block_size_l;
153 		}
154 		else {
155 			break;
156 		}
157 
158 		lmfs_put_block(bp);
159 		bp = read_extent_block(extent, *offset /
160 		    v_pri.logical_block_size_l);
161 	}
162 
163 	return bp;
164 }
165 
166 int read_inode(struct inode *i_node, struct dir_extent *extent, size_t offset,
167 	size_t *new_offset)
168 {
169 	struct iso9660_dir_record *dir_rec;
170 	struct buf *bp;
171 
172 	/* Find inode. */
173 	bp = fetch_inode(extent, &offset);
174 	if (bp == NULL)
175 		return EOF;
176 
177 	dir_rec = (struct iso9660_dir_record*)(b_data(bp) + offset %
178 	          v_pri.logical_block_size_l);
179 
180 	/* Parse basic ISO 9660 specs. */
181 	if (check_dir_record(dir_rec,
182 	    offset % v_pri.logical_block_size_l) != OK) {
183 		lmfs_put_block(bp);
184 		return EINVAL;
185 	}
186 
187 	memset(&i_node->i_stat, 0, sizeof(struct stat));
188 
189 	i_node->i_stat.st_ino = get_extent_absolute_block_id(extent,
190 	    offset / v_pri.logical_block_size_l) * v_pri.logical_block_size_l +
191 	    offset % v_pri.logical_block_size_l;
192 
193 	read_inode_iso9660(i_node, dir_rec);
194 
195 	/* Parse extensions. */
196 	read_inode_susp(i_node, dir_rec, bp,
197 	    offset % v_pri.logical_block_size_l);
198 
199 	offset += dir_rec->length;
200 	read_inode_extents(i_node, dir_rec, extent, &offset);
201 
202 	lmfs_put_block(bp);
203 	if (new_offset != NULL)
204 		*new_offset = offset;
205 	return OK;
206 }
207 
208 void read_inode_iso9660(struct inode *i,
209 	const struct iso9660_dir_record *dir_rec)
210 {
211 	char *cp;
212 
213 	/* Parse first extent. */
214 	if (dir_rec->data_length_l > 0) {
215 		assert(i->extent == NULL);
216 		i->extent = alloc_extent();
217 		i->extent->location = dir_rec->loc_extent_l +
218 		                      dir_rec->ext_attr_rec_length;
219 		i->extent->length = dir_rec->data_length_l /
220 		                    v_pri.logical_block_size_l;
221 		if (dir_rec->data_length_l % v_pri.logical_block_size_l)
222 			i->extent->length++;
223 
224 		i->i_stat.st_size = dir_rec->data_length_l;
225 	}
226 
227 	/* Parse timestamps (record date). */
228 	i->i_stat.st_atime = i->i_stat.st_mtime = i->i_stat.st_ctime =
229 	    i->i_stat.st_birthtime = date7_to_time_t(dir_rec->rec_date);
230 
231 	if ((dir_rec->file_flags & D_TYPE) == D_DIRECTORY) {
232 		i->i_stat.st_mode = S_IFDIR;
233 		i->i_stat.st_ino =
234 		    i->extent->location * v_pri.logical_block_size_l;
235 	}
236 	else
237 		i->i_stat.st_mode = S_IFREG;
238 	i->i_stat.st_mode |= 0555;
239 
240 	/* Parse file name. */
241 	if (dir_rec->file_id[0] == 0)
242 		strcpy(i->i_name, ".");
243 	else if (dir_rec->file_id[0] == 1)
244 		strcpy(i->i_name, "..");
245 	else {
246 		memcpy(i->i_name, dir_rec->file_id, dir_rec->length_file_id);
247 
248 		/* Truncate/ignore file version suffix. */
249 		cp = strchr(i->i_name, ';');
250 		if (cp != NULL)
251 			*cp = '\0';
252 		/* Truncate dot if file has no extension. */
253 		if (strchr(i->i_name, '.') + 1 == cp)
254 			*(cp-1) = '\0';
255 	}
256 
257 	/* Initialize stat. */
258 	i->i_stat.st_dev = fs_dev;
259 	i->i_stat.st_blksize = v_pri.logical_block_size_l;
260 	i->i_stat.st_blocks =
261 	    dir_rec->data_length_l / v_pri.logical_block_size_l;
262 	i->i_stat.st_nlink = 1;
263 }
264 
265 void read_inode_extents(struct inode *i,
266 	const struct iso9660_dir_record *dir_rec,
267 	struct dir_extent *extent, size_t *offset)
268 {
269 	struct buf *bp;
270 	struct iso9660_dir_record *extent_rec;
271 	struct dir_extent *cur_extent = i->extent;
272 	int done = FALSE;
273 
274 	/*
275 	 * No need to search extents if file is empty or has final directory
276 	 * record flag set.
277 	 */
278 	if (cur_extent == NULL ||
279 	    ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0))
280 		return;
281 
282 	while (!done) {
283 		bp = fetch_inode(extent, offset);
284 		if (bp == NULL)
285 			return;
286 
287 		bp = read_extent_block(extent,
288 		    *offset / v_pri.logical_block_size_l);
289 		extent_rec = (struct iso9660_dir_record*)(b_data(bp) +
290 		    *offset % v_pri.logical_block_size_l);
291 
292 		if (check_dir_record(dir_rec,
293 		    *offset % v_pri.logical_block_size_l) != OK) {
294 			lmfs_put_block(bp);
295 			return;
296 		}
297 
298 		/* Extent entries should share the same name. */
299 		if ((dir_rec->length_file_id == extent_rec->length_file_id) &&
300 		    (memcmp(dir_rec->file_id, extent_rec->file_id,
301 		    dir_rec->length_file_id) == 0)) {
302 			/* Add the extent at the end of the linked list. */
303 			assert(cur_extent->next == NULL);
304 			cur_extent->next = alloc_extent();
305 			cur_extent->next->location = dir_rec->loc_extent_l +
306 			    dir_rec->ext_attr_rec_length;
307 			cur_extent->next->length = dir_rec->data_length_l /
308 			    v_pri.logical_block_size_l;
309 			if (dir_rec->data_length_l % v_pri.logical_block_size_l)
310 				cur_extent->next->length++;
311 
312 			i->i_stat.st_size += dir_rec->data_length_l;
313 			i->i_stat.st_blocks += cur_extent->next->length;
314 
315 			cur_extent = cur_extent->next;
316 			*offset += extent_rec->length;
317 		}
318 		else
319 			done = TRUE;
320 
321 		/* Check if not last extent bit is not set. */
322 		if ((dir_rec->file_flags & D_NOT_LAST_EXTENT) == 0)
323 			done = TRUE;
324 
325 		lmfs_put_block(bp);
326 	}
327 }
328 
329 void read_inode_susp(struct inode *i, const struct iso9660_dir_record *dir_rec,
330 	struct buf *bp, size_t offset)
331 {
332 	int susp_offset, susp_size;
333 	struct rrii_dir_record rrii_data;
334 
335 	susp_offset = 33 + dir_rec->length_file_id;
336 	/* Get rid of padding byte. */
337 	if(dir_rec->length_file_id % 2 == 0) {
338 		susp_offset++;
339 	}
340 
341 	if(dir_rec->length - susp_offset >= 4) {
342 		susp_size = dir_rec->length - susp_offset;
343 
344 		/* Initialize record with known, sane data. */
345 		memcpy(rrii_data.mtime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
346 		memcpy(rrii_data.atime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
347 		memcpy(rrii_data.ctime, dir_rec->rec_date, ISO9660_SIZE_DATE7);
348 		memcpy(rrii_data.birthtime, dir_rec->rec_date,
349 		    ISO9660_SIZE_DATE7);
350 
351 		rrii_data.d_mode = i->i_stat.st_mode;
352 		rrii_data.uid    = 0;
353 		rrii_data.gid    = 0;
354 		rrii_data.rdev   = NO_DEV;
355 		rrii_data.file_id_rrip[0] = '\0';
356 		rrii_data.slink_rrip[0]   = '\0';
357 
358 		parse_susp_buffer(&rrii_data, b_data(bp)+offset+susp_offset,
359 		    susp_size);
360 
361 		/* Copy back data from rrii_dir_record structure. */
362 		i->i_stat.st_atime = date7_to_time_t(rrii_data.atime);
363 		i->i_stat.st_ctime = date7_to_time_t(rrii_data.ctime);
364 		i->i_stat.st_mtime = date7_to_time_t(rrii_data.mtime);
365 		i->i_stat.st_birthtime = date7_to_time_t(rrii_data.birthtime);
366 
367 		i->i_stat.st_mode = rrii_data.d_mode;
368 		i->i_stat.st_uid  = rrii_data.uid;
369 		i->i_stat.st_gid  = rrii_data.gid;
370 		i->i_stat.st_rdev = rrii_data.rdev;
371 
372 		if (rrii_data.file_id_rrip[0] != '\0')
373 			strlcpy(i->i_name, rrii_data.file_id_rrip,
374 			   sizeof(i->i_name));
375 		if (rrii_data.slink_rrip[0] != '\0')
376 			strlcpy(i->s_link, rrii_data.slink_rrip,
377 			   sizeof(i->s_link));
378 	}
379 }
380 
381 int check_dir_record(const struct iso9660_dir_record *d, size_t offset)
382 {
383 	/* Run some consistency check on a directory entry. */
384 	if ((d->length < 33) || (d->length_file_id < 1))
385 		return EINVAL;
386 	if (d->length_file_id + 32 > d->length)
387 		return EINVAL;
388 	if (offset + d->length > v_pri.logical_block_size_l)
389 		return EINVAL;
390 
391 	return OK;
392 }
393 
394 int check_inodes(void)
395 {
396 	/* Check whether there are no more inodes in use. Called on unmount. */
397 	int i;
398 
399 	for (i = 0; i < NR_INODE_RECORDS; i++)
400 		if (inodes[i].i_count > 0)
401 			return FALSE;
402 
403 	return TRUE;
404 }
405