[MKISOFS]
[reactos.git] / reactos / sdk / tools / mkisofs / schilytools / mkisofs / inode.c
1 /* @(#)inode.c 1.19 16/11/14 Copyright 2006-2016 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)inode.c 1.19 16/11/14 Copyright 2006-2015 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-2016 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
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
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
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
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->de_flags & RELOCATED_DIRECTORY)
213 continue;
214
215 /*
216 * skip resource files or file stream files
217 * XXX should we assign a standard link count == 1 instead?
218 */
219 if (s_entry->de_flags & RESOURCE_FORK)
220 continue;
221
222 /*
223 * Assign inodes to symbolic links.
224 * We never come here in case that we create correct inodes,
225 * except with UDF symlinks.
226 */
227 if (s_entry->dev == UNCACHED_DEVICE && s_entry->inode == UNCACHED_INODE) {
228 s_entry->dev = PREV_SESS_DEV;
229
230 /*
231 * With UDF symlinks, the starting_block is a
232 * valid inode number.
233 */
234 #ifdef UDF
235 if ((s_entry->de_flags & IS_SYMLINK) != 0 &&
236 create_udfsymlinks) {
237 #else
238 if ((s_entry->de_flags & IS_SYMLINK) != 0) {
239 #endif
240 s_entry->inode = s_entry->starting_block;
241 } else {
242 s_entry->inode = null_inodes--; /* Only used for caching */
243 if (correct_inodes)
244 comerrno(EX_BAD,
245 _("Implementation botch: Unhashed file '%s'.\n"),
246 s_entry->whole_name ?
247 s_entry->whole_name : s_entry->name);
248 }
249 }
250 s_hash = find_hash(s_entry);
251 if (s_hash) {
252 s_hash->nlink++;
253 } else {
254 add_hash(s_entry);
255 s_hash = find_hash(s_entry);
256 if (s_hash == NULL) {
257 if (s_entry->dev == UNCACHED_DEVICE &&
258 s_entry->inode == TABLE_INODE) {
259 continue;
260 }
261 comerrno(EX_BAD,
262 _("Implementation botch: File '%s' not hashed (dev/ino %llX/%llX).\n"),
263 s_entry->whole_name ?
264 s_entry->whole_name : s_entry->name,
265 (Llong)s_entry->dev,
266 (Llong)s_entry->inode);
267 }
268 s_hash->nlink++;
269 }
270 }
271 if (dpnt->subdir) {
272 compute_linkcount(dpnt->subdir);
273 }
274
275 dpnt = dpnt->next;
276 }
277 }
278
279 /*
280 * Assig the link count for non-directory type files to the value
281 * computed with compute_linkcount().
282 */
283 LOCAL void
284 assign_linkcount(dpnt)
285 struct directory *dpnt;
286 {
287 struct directory_entry *s_entry;
288 struct file_hash *s_hash;
289
290 while (dpnt) {
291 s_entry = dpnt->contents;
292 for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
293 if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
294 continue;
295 if (s_entry->de_flags & RELOCATED_DIRECTORY)
296 continue;
297 /*
298 * skip resource files or file stream files
299 */
300 if (s_entry->de_flags & RESOURCE_FORK)
301 continue;
302
303 s_hash = find_hash(s_entry);
304 if (s_hash) {
305 update_nlink(s_entry, s_hash->nlink);
306 } else {
307 if (s_entry->dev == UNCACHED_DEVICE &&
308 s_entry->inode == TABLE_INODE) {
309 continue;
310 }
311 comerrno(EX_BAD,
312 _("Implementation botch: File '%s' not hashed.\n"),
313 s_entry->whole_name ?
314 s_entry->whole_name : s_entry->name);
315 }
316 }
317 if (dpnt->subdir) {
318 assign_linkcount(dpnt->subdir);
319 }
320
321 dpnt = dpnt->next;
322 }
323 }
324
325 /*
326 * Rewrite the content of the RR inode field in the PX record.
327 */
328 LOCAL void
329 update_inode(s_entry, value)
330 struct directory_entry *s_entry;
331 int value;
332 {
333 unsigned char *pnt;
334 int len;
335
336 if (!rrip112)
337 return;
338
339 pnt = s_entry->rr_attributes;
340 len = s_entry->total_rr_attr_size;
341 pnt = parse_xa(pnt, &len, 0);
342 while (len >= 4) {
343 if (pnt[3] != 1 && pnt[3] != 2) {
344 errmsgno(EX_BAD,
345 _("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
346 pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
347 }
348 if (pnt[2] < 4) {
349 errmsgno(EX_BAD,
350 _("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
351 pnt[2], pnt, pnt[0], pnt[1]);
352 break;
353 }
354 if (pnt[0] == 'P' && pnt[1] == 'X') {
355 if ((pnt[2] & 0xFF) < 44) /* Paranoia */
356 return;
357 set_733((char *)pnt + 36, value);
358 break;
359 }
360 len -= pnt[2];
361 pnt += pnt[2];
362 }
363 }
364
365 /*
366 * Rewrite the content of the RR nlink field in the PX record.
367 */
368 LOCAL void
369 update_nlink(s_entry, value)
370 struct directory_entry *s_entry;
371 int value;
372 {
373 unsigned char *pnt;
374 int len;
375
376 pnt = s_entry->rr_attributes;
377 len = s_entry->total_rr_attr_size;
378 pnt = parse_xa(pnt, &len, 0);
379 while (len >= 4) {
380 if (pnt[3] != 1 && pnt[3] != 2) {
381 errmsgno(EX_BAD,
382 _("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
383 pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
384 }
385 if (pnt[2] < 4) {
386 errmsgno(EX_BAD,
387 _("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
388 pnt[2], pnt, pnt[0], pnt[1]);
389 break;
390 }
391 if (pnt[0] == 'P' && pnt[1] == 'X') {
392 set_733((char *)pnt + 12, value);
393 break;
394 }
395 len -= pnt[2];
396 pnt += pnt[2];
397 }
398 }
399
400 /*
401 * Set the link count for directories to 2 + number of sub-directories.
402 * This is done here for all diresctories except for "/..".
403 */
404 LOCAL int
405 update_dir_nlink(dpnt)
406 struct directory *dpnt;
407 {
408 struct directory *xpnt;
409 struct directory_entry *s_entry;
410 int i = 0;
411
412 while (dpnt) {
413 if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
414 dpnt = dpnt->next;
415 continue;
416 }
417 /*
418 * First, count up the number of subdirectories this dir has.
419 */
420 for (i = 0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next)
421 if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
422 i++;
423 /*
424 * Next check to see if we have any relocated directories in
425 * this directory. The nlink field will include these as
426 * real directories when they are properly relocated.
427 * In the non-rockridge disk, the relocated entries appear as
428 * zero length files.
429 */
430 for (s_entry = dpnt->contents; s_entry;
431 s_entry = s_entry->next) {
432 if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0 &&
433 (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) ==
434 0) {
435 i++;
436 }
437 }
438 /*
439 * Now update the field in the Rock Ridge entry.
440 */
441 update_nlink(dpnt->self, i + 2);
442
443 /*
444 * Update the '.' entry for this directory.
445 */
446 update_nlink(dpnt->contents, i + 2);
447
448 /*
449 * Update all of the '..' entries that point to this guy.
450 */
451 for (xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) {
452 update_nlink(xpnt->contents->next, i + 2);
453 }
454
455 if (dpnt->subdir)
456 update_dir_nlink(dpnt->subdir);
457 dpnt = dpnt->next;
458 }
459 return (i+2);
460 }