xref: /openbsd/gnu/usr.sbin/mkhybrid/src/volume.c (revision 09467b48)
1 /*
2 **	volume.c: prepare HFS volume for mkhybrid
3 **
4 **	James Pearson 17/7/97
5 **	modified JCP 29/7/97 to improve allocation sizes to cut
6 **	down on wasted space. Now uses the HFS "allocation" size rounded
7 **	up to the nearest 2048 bytes. Savings can be significant with
8 **	a large volume containing lots of smallish files.
9 **
10 **	Updated for v1.12 - now uses the built in RELOCATED_DIRECTORY
11 **	flag for finding the real directory location JCP 8/1/97
12 */
13 
14 #ifdef APPLE_HYB
15 
16 #include "config.h"
17 #include "mkisofs.h"
18 #include "volume.h"
19 #include "write.h"
20 #include <errno.h>
21 
22 static hfsvol *vol_save = 0;	/* used to "destroy" an HFS volume */
23 
24 int DECL(copy_to_mac_vol, (hfsvol *, struct directory *));
25 
26 /*
27 **	AlcSiz: find allocation size for given volume size
28 */
29 int
30 AlcSiz(int vlen)
31 {
32 	int	lpa, drAlBlkSiz;
33 
34 	/* code extracted from hfs_format() */
35 	lpa = 1 + vlen / 65536;
36 	drAlBlkSiz = lpa * HFS_BLOCKSZ;
37 
38 	/* now set our "allocation size" to the allocation block rounded
39 	   up to the nearest SECTOR_SIZE (2048 bytes)  */
40 	drAlBlkSiz = V_ROUND_UP(drAlBlkSiz, SECTOR_SIZE);
41 
42 	return(drAlBlkSiz);
43 }
44 
45 /*
46 **	XClpSiz: find the default size of the catalog/extent file
47 */
48 int
49 XClpSiz(int vlen)
50 {
51 	int	olpa, lpa, drNmAlBlks, drAlBlkSiz;
52 	int	vbmsz, drXTClpSiz;
53 
54 	/* code extracted from hfs_format() */
55 
56 	/* get the lpa from our calculated allocation block size */
57 	drAlBlkSiz = AlcSiz(vlen);
58 	lpa = drAlBlkSiz/HFS_BLOCKSZ;
59 
60 	vbmsz = (vlen / lpa + 4095) / 4096;
61 	drNmAlBlks = (vlen - 5 - vbmsz) / lpa;
62 	drXTClpSiz = drNmAlBlks / 128 * drAlBlkSiz;
63 
64 	/* make allowances because we have possibly rounded up the
65 	   allocation size */
66 
67 	/* get the "original" lpa " */
68 	olpa = 1 + vlen / 65536;
69 
70 	/* adjust size upwards */
71 	drXTClpSiz = (drXTClpSiz*lpa)/olpa;
72 
73 	/* round up to the nearest alloaction size */
74 	drXTClpSiz = V_ROUND_UP(drXTClpSiz, drAlBlkSiz);
75 
76 	return(drXTClpSiz);
77 }
78 
79 /*
80 **	get_vol_size: get the size of the volume including the extent/catalog
81 */
82 int
83 get_vol_size(int vblen)
84 {
85 	int	drXTClpSiz, drAlBlkSiz;
86 	int	new_vblen;
87 
88 	/* try to estimate a "volume size" based on the code
89 	   in hfs_format - we need the size of the catalog/extents
90 	   and Desktop files included in the volume, as we add this
91 	   to the end of the ISO volume */
92 
93 	drXTClpSiz = XClpSiz(vblen);
94 	drAlBlkSiz = AlcSiz(vblen);
95 
96 	/* catalog file is set at CTC times (default twice) the extents file
97 	   size - hence the (ctc_size + 1) below. The Desktop starts of the
98 	   same size as the "clump size" == 4 x drAlBlkSiz, plus a spare
99 	   drAlBlkSiz for the alternative MDB */
100 
101 	new_vblen = vblen + ((hce->ctc_size + 1)*drXTClpSiz + 5*drAlBlkSiz)/HFS_BLOCKSZ;
102 
103 	return (new_vblen);
104 }
105 
106 /*
107 **	write_fork: "write" file data to the volume
108 **
109 **	This is used to update the HFS file internal structures
110 **	but no data is actually written (it's trapped deep down in
111 **	libhfs).
112 */
113 int
114 write_fork(hfsfile *hfp, long tot)
115 {
116 	char	blk[HFS_BLOCKSZ];
117 	unsigned short start;
118 	long	len;
119 
120 	len = tot;
121 	/* we need to know where this fork starts */
122 	start = hfs_get_drAllocPtr(hfp);
123 
124 	/* loop through the data a block at a time */
125 	while (len >= HFS_BLOCKSZ)
126 	{
127 	    if(hfs_write(hfp, blk, HFS_BLOCKSZ) < 0)
128 		return(-1);
129 	    len -= HFS_BLOCKSZ;
130 	}
131 	/* write out anything left */
132 	if (len)
133 	    if(hfs_write(hfp, blk, len) < 0)
134 		return(-1);
135 
136 	/* set the start of the allocation search to be immediately
137 	   after this fork */
138 	hfs_set_drAllocPtr(hfp, start, tot);
139 
140 	return(0);
141 }
142 
143 /*
144 **	make_mac_volume: "create" an HFS volume using the ISO data
145 **
146 **	The HFS volume structures are set up (but no data is written yet).
147 **
148 **	ISO volumes have a allocation size of 2048 bytes - regardless
149 **	of the size of the volume. HFS allocation size is depends on volume
150 **	size, so we may have to update the ISO structures to add in any
151 **	padding.
152 */
153 int FDECL2(make_mac_volume, struct directory *, dpnt, int, start_extent)
154 {
155 	char vol_name[HFS_MAX_VLEN+1];	/* Mac volume name */
156 	hfsvol *vol;			/* Mac volume */
157 	int vlen, vblen;		/* vol length (bytes, blocks) */
158 	int Csize, lastCsize;		/* allocation sizes */
159 	int ret = 0;			/* return value */
160 	int loop = 1;
161 
162 	/* umount volume if we have had a previous attempt */
163 	if (vol_save)
164 	    if (hfs_umount(vol_save, 0) < 0)
165 		return (-1);
166 
167 	/* set the default clump size to the ISO block size */
168 	lastCsize = SECTOR_SIZE;
169 
170 	if (verbose > 1)
171 	    fprintf(stderr, "Creating HFS Volume info\n");
172 
173 	/* name or copy ISO volume name to Mac Volume name */
174 	strncpy(vol_name, hfs_volume_id ? hfs_volume_id : volume_id, HFS_MAX_VLEN);
175 	vol_name[HFS_MAX_VLEN] = '\0';
176 
177 	/* get initial size of HFS volume (size of ISO volume) */
178 	vblen = last_extent * BLK_CONV;
179 
180 	/* add on size of extents/catalog file, but this may mean
181 	   the allocation size will change, so loop round until the allocation
182 	   size doesn't change */
183 	while (loop) {
184 	    hce->XTCsize = XClpSiz(vblen);
185 	    vblen = get_vol_size(vblen);
186 	    Csize = AlcSiz(vblen);
187 
188 	    if (Csize == lastCsize) {
189 		/* allocation size hasn't changed, so carry on */
190 		loop = 0;
191 	    }
192 	    else {
193 		/* allocation size has changed, so update ISO volume size */
194 		if ((vlen = get_adj_size(Csize)) < 0) {
195 		    snprintf(hce->error, ERROR_SIZE,
196 		    	"too many files for HFS volume");
197 		    return (-1);
198 		}
199 		vlen += V_ROUND_UP(start_extent * SECTOR_SIZE, Csize);
200 		vblen = vlen /  HFS_BLOCKSZ;
201 		lastCsize = Csize;
202 	    }
203 	}
204 
205 	/* set vlen to size in bytes */
206 /*	vlen = hce->hfs_vol_size = vblen * HFS_BLOCKSZ; */
207 	/* take off the label/map size */
208 	vblen -= hce->hfs_map_size;
209 	vlen = hce->hfs_vol_size = vblen * HFS_BLOCKSZ;
210 
211 	/* set the default allocation size for libhfs */
212 	hce->Csize = Csize;
213 
214 	/* format and mount the "volume" */
215 	if (hfs_format(hce, 0, vol_name) < 0)
216 	{
217 	    snprintf(hce->error, ERROR_SIZE, "can't HFS format %s",vol_name);
218 	    return(-1);
219 	}
220 
221 	/* update the ISO structures with new start extents and any padding
222 	   required */
223 	if (Csize != SECTOR_SIZE) {
224 	    last_extent = adj_size(Csize, start_extent, hce->hfs_hdr_size + hce->hfs_map_size);
225 	    adj_size_other(dpnt);
226 	}
227 
228 	if ((vol = hfs_mount(hce, 0, 0)) == 0)
229 	{
230 	    snprintf(hce->error, ERROR_SIZE, "can't HFS mount %s",vol_name);
231 	    return(-1);
232 	}
233 
234 	/* save the volume for possible later use */
235 	vol_save = vol;
236 
237 	/* Recursively "copy" the files to the volume - we need to
238 	   know the first allocation block in the volume as starting blocks
239 	   of files are relative to this.
240 	*/
241 	ret = copy_to_mac_vol(vol, dpnt);
242 	if (ret < 0)
243 	    return(ret);
244 
245 	/* make the Desktop files - I *think* this stops the Mac
246 	   rebuilding the desktop when the CD is mounted on a Mac
247 	   These will be ignored if they already exist */
248 	if (create_dt)
249 	    ret = make_desktop(vol, last_extent*BLK_CONV);
250 	if (ret < 0)
251 	    return(ret);
252 
253 	/* close the volume */
254 	if (hfs_flush(vol) < 0)
255 	    return(-1);
256 
257 	/* unmount and set the start blocks for the catalog/extents files */
258 	if (hfs_umount(vol, last_extent*BLK_CONV) < 0)
259 	    return(-1);
260 
261 	return(Csize);
262 }
263 
264 #define TEN 10		/* well, it is! */
265 #define LCHAR "_"
266 
267 /*	copy_to_mac_vol: copy all files in a directory to corresponding
268 **			 Mac folder.
269 **
270 **	Files are copied recursively to corresponding folders on the Mac
271 **	volume. The caller routine needs to do a hfs_chdir before calling this
272 **	routine.
273 */
274 int FDECL2(copy_to_mac_vol, hfsvol *, vol, struct directory *, node)
275 {
276 	struct directory_entry * s_entry;	/* ISO directory entry */
277 	struct directory_entry * s_entry1;	/* tmp ISO directory entry */
278 	struct directory *dpnt;			/* ISO directory */
279 
280 	hfsfile *hfp;				/* HFS file */
281 	hfsdirent *ent;				/* HFS file entities */
282 	long id;				/* current HFS folder */
283 	long dext, rext;			/* real data/rsrc start blk */
284 	int ret;				/* result code */
285 	int new_name;				/* HFS file has modified name */
286 
287 	int	tens;
288 	int	digits;
289 	int	i;
290 
291 	/* store the current HFS directory ID */
292 	if ((id = hfs_getcwd(vol)) == 0)
293 	    return(-1);
294 
295 	if (verbose > 1)
296 	    fprintf(stderr,"HFS scanning %s\n", node->whole_name);
297 
298 	/* loop through the ISO directory entries and process files */
299 	for(s_entry = node->contents; s_entry; s_entry = s_entry->next)
300 	{
301 	    /* ignore directory and associated (rsrc) files */
302 	    if(s_entry->isorec.flags[0])
303 		    continue;
304 
305 	    /* ignore any non-Mac type file */
306 	    if(!s_entry->hfs_ent)
307 		    continue;
308 
309 #ifdef DEBUG
310 	    fprintf(stderr," Name = %s", s_entry->whole_name);
311 	    fprintf(stderr,"   Startb =  %d\n", s_entry->starting_block);
312 #endif /* DEBUG */
313 
314 	    ent = s_entry->hfs_ent;
315 
316 	    /* create file */
317 	    i = HFS_MAX_FLEN - strlen(ent->name);
318 	    new_name = 0;
319 	    tens = TEN;
320 	    digits = 1;
321 
322 	    while (1)
323 	    {
324 		/* try to open file - if it exists, then append '_' to
325 		   the name and try again */
326 		errno = 0;
327 		if ((hfs_create(vol, ent->name, ent->type, ent->creator)) < 0)
328 		{
329 		    if (errno != EEXIST )
330 		    {
331 			/* not an "exist" error, or we can't append as
332 			   the filename is already HFS_MAX_FLEN chars */
333 			snprintf(hce->error, ERROR_SIZE,
334 				"can't HFS create file %s",
335 				s_entry->whole_name);
336 			return(-1);
337 		    }
338 		    else if (i == 0)
339 		    {
340 			/* File name at max HFS length - make unique name */
341 			if (!new_name) new_name++;
342 
343 			sprintf(ent->name + HFS_MAX_FLEN - digits - 1,
344 					"%s%d", LCHAR, new_name);
345 			new_name++;
346 			if (new_name == tens) {
347 			    tens *= TEN;
348 			    digits++;
349 			}
350 		    }
351 		    else
352 		    {
353 			/* append '_' to get new name */
354 			strcat(ent->name, LCHAR);
355 			i--;
356 			new_name = 1;
357 		    }
358 		}
359 		else
360 		    break;
361 	    }
362 
363 	    /* warn that we have a new name */
364 	    if (new_name && verbose > 0)
365 	    {
366 		fprintf(stderr, "Using HFS name: %s for %s\n", ent->name,
367 			s_entry->whole_name);
368 	    }
369 
370 	    /* open file */
371 	    if ((hfp = hfs_open(vol, ent->name)) == 0)
372 	    {
373 		snprintf(hce->error, ERROR_SIZE, "can't HFS open %s",
374 		    s_entry->whole_name);
375 		return(-1);
376 	    }
377 
378 	    /* if it has a data fork, then "write" it out */
379 	    if (ent->dsize)
380 		write_fork(hfp, ent->dsize);
381 
382 	    /* if it has a resource fork, set the fork and "write" it out */
383 	    if (ent->rsize)
384 	    {
385 		if ((hfs_setfork(hfp, 1)) < 0)
386 		    return(-1);
387 		write_fork(hfp, ent->rsize);
388 	    }
389 
390 	    /* update any HFS file attributes */
391 	    if ((hfs_fsetattr(hfp, ent)) < 0)
392 	    {
393 		snprintf(hce->error, ERROR_SIZE, "can't HFS set attributes %s",
394 			s_entry->whole_name);
395 		return(-1);
396 	    }
397 
398 	    /* get the ISO starting block of data fork (may be zero)
399 	       and convert to the equivalent HFS block */
400 	    if (ent->dsize)
401 		dext = s_entry->starting_block * BLK_CONV;
402 	    else
403 		dext = 0;
404 
405 	    /* if the file has a resource fork (associated file), get it's
406 	       ISO starting block and convert as above */
407 	    if (s_entry->assoc && ent->rsize)
408 		rext = s_entry->assoc->starting_block * BLK_CONV;
409 	    else
410 		rext = 0;
411 
412 	    /* close the file and update the starting blocks */
413 	    if (hfs_close(hfp, dext, rext) < 0)
414 	    {
415 		snprintf(hce->error, ERROR_SIZE, "can't HFS close file %s",
416 			s_entry->whole_name);
417 		return(-1);
418 	    }
419 	}
420 
421 	/* process sub-directories  - have a slight problem here,
422 	   if the directory had been relocated, then we need to find
423 	   the real directory - we do this by first finding the real
424 	   directory_entry, and then finding it's directory info */
425 
426 	/* following code taken from joliet.c */
427 	for(s_entry=node->contents;s_entry;s_entry=s_entry->next)
428 	{
429 	    if((s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
430 	    {
431 		/* if the directory has been reloacted, then search the
432 		   relocated directory for the real entry */
433 		for(s_entry1=reloc_dir->contents;s_entry1;s_entry1=s_entry1->next)
434 		{
435 		    if(s_entry1->parent_rec == s_entry)
436 			break;
437 		}
438 
439 		/* have a problem - can't find the real directory */
440 		if(s_entry1 == NULL)
441 		{
442 		    snprintf(hce->error, ERROR_SIZE,
443 		    	"can't locate relocated directory %s",
444 			s_entry->whole_name);
445 		    return(-1);
446 		}
447 	    }
448 	    else
449 		s_entry1 = s_entry;
450 
451 	    /* now have the correct entry - now find the actual directory */
452 	    if ((s_entry1->isorec.flags[0] & 2) && strcmp(s_entry1->name,".") && strcmp(s_entry1->name,".."))
453 	    {
454 		if((s_entry->de_flags & RELOCATED_DIRECTORY) != 0)
455 		    dpnt = reloc_dir->subdir;
456 		else
457 		    dpnt = node->subdir;
458 
459 		while(1)
460 		{
461 		    if (dpnt->self == s_entry1)
462 			break;
463 		    dpnt = dpnt->next;
464 		    if(!dpnt)
465 		    {
466 			snprintf(hce->error, ERROR_SIZE,
467 			    "can't find directory location %s",
468 			    s_entry1->whole_name);
469 			return (-1);
470 		    }
471 		}
472 		/* now have the correct directory - so do the HFS stuff */
473 		ent = dpnt->hfs_ent;
474 
475 		/* if we don't have hfs entries, then this is a "deep"
476 		   directory - this will be processed later */
477 		if (!ent)
478 		    continue;
479 
480 		/* make sub-folder */
481 		i = HFS_MAX_FLEN - strlen(ent->name);
482 		new_name = 0;
483 		tens = TEN;
484 		digits = 1;
485 
486 		while (1)
487 		{
488 		    /* try to create new directory  - if it exists, then
489 		       append '_' to the name and try again */
490 		    errno = 0;
491 		    if (hfs_mkdir(vol, ent->name) < 0)
492 		    {
493 			if (errno != EEXIST)
494 			{
495 			    /* not an "exist" error, or we can't append as
496 			       the filename is already HFS_MAX_FLEN chars */
497 			    snprintf(hce->error, ERROR_SIZE,
498 			    	"can't HFS create folder %s",
499 				s_entry->whole_name);
500 			    return(-1);
501 			}
502 			else if (i == 0)
503 			{
504 			    /* File name at max HFS length - make unique name */
505 			    if (!new_name) new_name++;
506 
507 			    sprintf(ent->name + HFS_MAX_FLEN - digits - 1,
508 					"%s%d", LCHAR, new_name);
509 			    new_name++;
510 			    if (new_name == tens) {
511 				tens *= TEN;
512 				digits++;
513 			    }
514 			}
515 			else
516 			{
517 			    /* append '_' to get new name */
518 			    strcat(ent->name, LCHAR);
519 			    i--;
520 			    new_name = 1;
521 			}
522 		    }
523 		    else
524 			break;
525 		}
526 
527 		/* warn that we have a new name */
528 		if (new_name && verbose > 0)
529 		{
530 		    fprintf(stderr, "Using HFS name: %s for %s\n", ent->name,
531 			s_entry->whole_name);
532 		}
533 
534 		/* see if we need to "bless" this folder */
535 		if (hfs_bless && strcmp(s_entry->whole_name, hfs_bless) == 0) {
536 		    hfs_stat(vol, ent->name, ent);
537 		    hfs_vsetbless(vol, ent->cnid);
538 		    if (verbose > 0) {
539 			fprintf(stderr, "Blessing %s (%s)\n",
540 				ent->name, s_entry->whole_name);
541 		    }
542 		    /* stop any further checks */
543 		    hfs_bless = NULL;
544 		}
545 
546 		/* change to sub-folder */
547 		if (hfs_chdir(vol, ent->name) < 0)
548 		    return(-1);
549 
550 		/* recursively copy files ... */
551 		ret = copy_to_mac_vol(vol, dpnt);
552 		if (ret < 0)
553 		    return(ret);
554 
555 		/* change back to this folder */
556 		if (hfs_setcwd(vol, id) < 0)
557 		    return(-1);
558 	    }
559 	}
560 
561 	return(0);
562 }
563 #endif /* APPLE_HYB */
564 
565