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
22static hfsvol *vol_save = 0;	/* used to "destroy" an HFS volume */
23
24int DECL(copy_to_mac_vol, (hfsvol *, struct directory *));
25
26/*
27**	AlcSiz: find allocation size for given volume size
28*/
29static int
30AlcSiz(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*/
48static int
49XClpSiz(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*/
82static int
83get_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*/
113int
114write_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*/
153int 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*/
274int 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