1 /* @(#)inode.c 1.20 18/05/14 Copyright 2006-2018 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)inode.c 1.20 18/05/14 Copyright 2006-2016 J. Schilling";
6 #endif
7 /*
8 * Inode and link count handling for ISO-9660/RR
9 *
10 * This module computes and sets up a RR link count that reflects
11 * the name-count for files/directories in the ISO-9660/RR image.
12 * This module also assigns inode numbers tp all files/directories
13 * using either the RRip-112 protocol or a mkisofs specific method
14 * of asigning the related number to the "extent" field in the ISO
15 * directory record.
16 *
17 * Copyright (c) 2006-2018 J. Schilling
18 */
19 /*
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License version 2
22 * as published by the Free Software Foundation.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License along with
30 * this program; see the file COPYING. If not, write to the Free Software
31 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
32 */
33
34 #include "mkisofs.h"
35 #include <schily/schily.h>
36
37 /*
38 * Highest inode value we assign in this session.
39 */
40 LOCAL UInt32_t null_ino_high;
41
42 EXPORT void do_inode __PR((struct directory *dpnt));
43 EXPORT void do_dir_nlink __PR((struct directory *dpnt));
44 LOCAL void assign_inodes __PR((struct directory *dpnt));
45 LOCAL void compute_linkcount __PR((struct directory *dpnt));
46 LOCAL void assign_linkcount __PR((struct directory *dpnt));
47 LOCAL void update_inode __PR((struct directory_entry *s_entry, int value));
48 LOCAL void update_nlink __PR((struct directory_entry *s_entry, int value));
49 LOCAL int update_dir_nlink __PR((struct directory *dpnt));
50
51 /*
52 * Inode/hard link related stuff for non-directory type files.
53 */
54 EXPORT void
do_inode(dpnt)55 do_inode(dpnt)
56 struct directory *dpnt;
57 {
58 null_ino_high = null_inodes;
59
60 if (correct_inodes)
61 assign_inodes(root);
62
63 #ifdef UDF
64 if (!use_RockRidge && !use_udf)
65 return;
66 #else
67 if (!use_RockRidge)
68 return;
69 #endif
70 if (!cache_inodes) /* Never FALSE if correct_inodes TRUE */
71 return;
72
73 compute_linkcount(dpnt);
74 if (use_RockRidge) /* If we have Rock Ridge extensions, */
75 assign_linkcount(dpnt); /* reassign computed linkcount in RR */
76
77 if (null_inodes < last_extent)
78 comerrno(EX_BAD, _("Inode number overflow, too many files in file system.\n"));
79 }
80
81 /*
82 * Set the link count for directories to 2 + number of sub-directories.
83 */
84 EXPORT void
do_dir_nlink(dpnt)85 do_dir_nlink(dpnt)
86 struct directory *dpnt;
87 {
88 int rootlinks;
89
90 if (!use_RockRidge)
91 return;
92
93 /*
94 * Update everything except "/..".
95 */
96 rootlinks = update_dir_nlink(dpnt);
97 if (reloc_dir)
98 rootlinks--; /* rr_moved is hidden */
99 /*
100 * Update "/." now.
101 */
102 update_nlink(dpnt->contents, rootlinks);
103 /*
104 * Update "/.." now.
105 */
106 update_nlink(dpnt->contents->next, rootlinks);
107 }
108
109 /*
110 * Assign inode numbers to files of zero size and to symlinks.
111 */
112 LOCAL void
assign_inodes(dpnt)113 assign_inodes(dpnt)
114 struct directory *dpnt;
115 {
116 struct directory_entry *s_entry;
117 struct file_hash *s_hash;
118
119 while (dpnt) {
120 s_entry = dpnt->contents;
121 for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
122 if (s_entry->starting_block == 0) {
123 s_hash = find_hash(s_entry);
124 /* find_directory_hash() ? */
125 if (s_hash)
126 s_entry->starting_block = s_hash->starting_block;
127 }
128 if (s_entry->starting_block == 0 && s_entry->size != 0) {
129 unsigned int e = get_733((char *)s_entry->isorec.extent);
130
131 if (e != 0) {
132 errmsgno(EX_BAD,
133 _("Implementation botch, fetching extend %d for %s from dir entry.\n"),
134 e, s_entry->whole_name);
135 }
136 }
137 if (use_RockRidge && s_entry->starting_block > 0)
138 update_inode(s_entry, s_entry->starting_block);
139
140 /*
141 * Be careful: UDF Symlinks have size != 0, then
142 * s_hash->starting_block is a valid inode number.
143 */
144 if (s_entry->size != 0)
145 continue;
146 #ifdef UDF
147 if ((s_entry->de_flags & IS_SYMLINK) != 0 &&
148 create_udfsymlinks)
149 continue;
150 #else
151 if ((s_entry->de_flags & IS_SYMLINK) != 0)
152 continue;
153 #endif
154
155 if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
156 continue;
157
158 /*
159 * Assign inodes to symbolic links.
160 */
161 if (s_entry->dev == UNCACHED_DEVICE && s_entry->inode == UNCACHED_INODE) {
162 s_entry->dev = PREV_SESS_DEV;
163 s_entry->inode = null_inodes;
164 }
165 s_hash = find_hash(s_entry);
166 if (s_hash) {
167 /*
168 * Paranoia: Check for hashed files without proper inode #.
169 */
170 if (s_hash->starting_block <= last_extent)
171 comerrno(EX_BAD,
172 _("Implementation botch: Hashed file '%s' has illegal inode %u.\n"),
173 s_entry->whole_name ?
174 s_entry->whole_name : s_entry->name,
175 s_hash->starting_block);
176 set_733((char *)s_entry->isorec.extent, s_hash->starting_block);
177 s_entry->starting_block = s_hash->starting_block;
178 } else {
179 s_entry->starting_block = null_inodes--;
180 set_733((char *)s_entry->isorec.extent, s_entry->starting_block);
181 add_hash(s_entry);
182 }
183 if (use_RockRidge)
184 update_inode(s_entry, s_entry->starting_block);
185 }
186 if (dpnt->subdir) {
187 assign_inodes(dpnt->subdir);
188 }
189
190 dpnt = dpnt->next;
191 }
192 }
193
194 /*
195 * Compute the link count for non-directory type files.
196 */
197 LOCAL void
compute_linkcount(dpnt)198 compute_linkcount(dpnt)
199 struct directory *dpnt;
200 {
201 struct directory_entry *s_entry;
202 struct file_hash *s_hash;
203
204 while (dpnt) {
205 s_entry = dpnt->contents;
206 for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
207 /*
208 * Skip directories.
209 */
210 if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
211 continue;
212 if (s_entry->isorec.flags[0] & ISO_MULTIEXTENT)
213 continue;
214 if (s_entry->de_flags & RELOCATED_DIRECTORY)
215 continue;
216
217 /*
218 * skip resource files or file stream files
219 * XXX should we assign a standard link count == 1 instead?
220 */
221 if (s_entry->de_flags & RESOURCE_FORK)
222 continue;
223
224 /*
225 * Assign inodes to symbolic links.
226 * We never come here in case that we create correct inodes,
227 * except with UDF symlinks.
228 */
229 if (s_entry->dev == UNCACHED_DEVICE && s_entry->inode == UNCACHED_INODE) {
230 s_entry->dev = PREV_SESS_DEV;
231
232 /*
233 * With UDF symlinks, the starting_block is a
234 * valid inode number.
235 */
236 #ifdef UDF
237 if ((s_entry->de_flags & IS_SYMLINK) != 0 &&
238 create_udfsymlinks) {
239 #else
240 if ((s_entry->de_flags & IS_SYMLINK) != 0) {
241 #endif
242 s_entry->inode = s_entry->starting_block;
243 } else {
244 s_entry->inode = null_inodes--; /* Only used for caching */
245 if (correct_inodes)
246 comerrno(EX_BAD,
247 _("Implementation botch: Unhashed file '%s'.\n"),
248 s_entry->whole_name ?
249 s_entry->whole_name : s_entry->name);
250 }
251 }
252 s_hash = find_hash(s_entry);
253 if (s_hash) {
254 s_hash->nlink++;
255 } else {
256 add_hash(s_entry);
257 s_hash = find_hash(s_entry);
258 if (s_hash == NULL) {
259 if (s_entry->dev == UNCACHED_DEVICE &&
260 s_entry->inode == TABLE_INODE) {
261 continue;
262 }
263 comerrno(EX_BAD,
264 _("Implementation botch: File '%s' not hashed (dev/ino %llX/%llX).\n"),
265 s_entry->whole_name ?
266 s_entry->whole_name : s_entry->name,
267 (Llong)s_entry->dev,
268 (Llong)s_entry->inode);
269 }
270 s_hash->nlink++;
271 }
272 }
273 if (dpnt->subdir) {
274 compute_linkcount(dpnt->subdir);
275 }
276
277 dpnt = dpnt->next;
278 }
279 }
280
281 /*
282 * Assig the link count for non-directory type files to the value
283 * computed with compute_linkcount().
284 */
285 LOCAL void
assign_linkcount(dpnt)286 assign_linkcount(dpnt)
287 struct directory *dpnt;
288 {
289 struct directory_entry *s_entry;
290 struct file_hash *s_hash;
291
292 while (dpnt) {
293 s_entry = dpnt->contents;
294 for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
295 if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
296 continue;
297 if (s_entry->isorec.flags[0] & ISO_MULTIEXTENT)
298 continue;
299 if (s_entry->de_flags & RELOCATED_DIRECTORY)
300 continue;
301 /*
302 * skip resource files or file stream files
303 */
304 if (s_entry->de_flags & RESOURCE_FORK)
305 continue;
306
307 s_hash = find_hash(s_entry);
308 if (s_hash) {
309 update_nlink(s_entry, s_hash->nlink);
310 } else {
311 if (s_entry->dev == UNCACHED_DEVICE &&
312 s_entry->inode == TABLE_INODE) {
313 continue;
314 }
315 comerrno(EX_BAD,
316 _("Implementation botch: File '%s' not hashed.\n"),
317 s_entry->whole_name ?
318 s_entry->whole_name : s_entry->name);
319 }
320 }
321 if (dpnt->subdir) {
322 assign_linkcount(dpnt->subdir);
323 }
324
325 dpnt = dpnt->next;
326 }
327 }
328
329 /*
330 * Rewrite the content of the RR inode field in the PX record.
331 */
332 LOCAL void
update_inode(s_entry,value)333 update_inode(s_entry, value)
334 struct directory_entry *s_entry;
335 int value;
336 {
337 unsigned char *pnt;
338 int len;
339
340 if (!rrip112)
341 return;
342
343 pnt = s_entry->rr_attributes;
344 len = s_entry->total_rr_attr_size;
345 pnt = parse_xa(pnt, &len, 0);
346 while (len >= 4) {
347 if (pnt[3] != 1 && pnt[3] != 2) {
348 errmsgno(EX_BAD,
349 _("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
350 pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
351 }
352 if (pnt[2] < 4) {
353 errmsgno(EX_BAD,
354 _("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
355 pnt[2], pnt, pnt[0], pnt[1]);
356 break;
357 }
358 if (pnt[0] == 'P' && pnt[1] == 'X') {
359 if ((pnt[2] & 0xFF) < 44) /* Paranoia */
360 return;
361 set_733((char *)pnt + 36, value);
362 break;
363 }
364 len -= pnt[2];
365 pnt += pnt[2];
366 }
367 }
368
369 /*
370 * Rewrite the content of the RR nlink field in the PX record.
371 */
372 LOCAL void
update_nlink(s_entry,value)373 update_nlink(s_entry, value)
374 struct directory_entry *s_entry;
375 int value;
376 {
377 unsigned char *pnt;
378 int len;
379
380 pnt = s_entry->rr_attributes;
381 len = s_entry->total_rr_attr_size;
382 pnt = parse_xa(pnt, &len, 0);
383 while (len >= 4) {
384 if (pnt[3] != 1 && pnt[3] != 2) {
385 errmsgno(EX_BAD,
386 _("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
387 pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
388 }
389 if (pnt[2] < 4) {
390 errmsgno(EX_BAD,
391 _("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
392 pnt[2], pnt, pnt[0], pnt[1]);
393 break;
394 }
395 if (pnt[0] == 'P' && pnt[1] == 'X') {
396 set_733((char *)pnt + 12, value);
397 break;
398 }
399 len -= pnt[2];
400 pnt += pnt[2];
401 }
402 }
403
404 /*
405 * Set the link count for directories to 2 + number of sub-directories.
406 * This is done here for all diresctories except for "/..".
407 */
408 LOCAL int
update_dir_nlink(dpnt)409 update_dir_nlink(dpnt)
410 struct directory *dpnt;
411 {
412 struct directory *xpnt;
413 struct directory_entry *s_entry;
414 int i = 0;
415
416 while (dpnt) {
417 if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
418 dpnt = dpnt->next;
419 continue;
420 }
421 /*
422 * First, count up the number of subdirectories this dir has.
423 */
424 for (i = 0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next)
425 if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
426 i++;
427 /*
428 * Next check to see if we have any relocated directories in
429 * this directory. The nlink field will include these as
430 * real directories when they are properly relocated.
431 * In the non-rockridge disk, the relocated entries appear as
432 * zero length files.
433 */
434 for (s_entry = dpnt->contents; s_entry;
435 s_entry = s_entry->next) {
436 if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0 &&
437 (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) ==
438 0) {
439 i++;
440 }
441 }
442 /*
443 * Now update the field in the Rock Ridge entry.
444 */
445 update_nlink(dpnt->self, i + 2);
446
447 /*
448 * Update the '.' entry for this directory.
449 */
450 update_nlink(dpnt->contents, i + 2);
451
452 /*
453 * Update all of the '..' entries that point to this guy.
454 */
455 for (xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) {
456 update_nlink(xpnt->contents->next, i + 2);
457 }
458
459 if (dpnt->subdir)
460 update_dir_nlink(dpnt->subdir);
461 dpnt = dpnt->next;
462 }
463 return (i+2);
464 }
465