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