udf.c revision 1.27
1/* $NetBSD: udf.c,v 1.27 2022/04/26 13:27:24 reinoud Exp $ */
2
3/*
4 * Copyright (c) 2006, 2008, 2013, 2021, 2022 Reinoud Zandijk
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28#if HAVE_NBTOOL_CONFIG_H
29#include "nbtool_config.h"
30#endif
31
32#include <sys/cdefs.h>
33__RCSID("$NetBSD: udf.c,v 1.27 2022/04/26 13:27:24 reinoud Exp $");
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <errno.h>
39#include <time.h>
40#include <assert.h>
41#include <err.h>
42#include <unistd.h>
43#include <fcntl.h>
44#include <math.h>
45#include <sys/types.h>
46#include <sys/param.h>
47#include <sys/stat.h>
48#include <util.h>
49
50#if !HAVE_NBTOOL_CONFIG_H
51#define _EXPOSE_MMC
52#include <sys/cdio.h>
53#else
54#include "udf/cdio_mmc_structs.h"
55#endif
56
57#if !HAVE_NBTOOL_CONFIG_H
58#define HAVE_STRUCT_TM_TM_GMTOFF
59#endif
60
61#include "makefs.h"
62#include "udf_core.h"
63#include "newfs_udf.h"
64
65/* identification */
66#define IMPL_NAME		"*NetBSD makefs 10.0"
67#define APP_VERSION_MAIN	0
68#define APP_VERSION_SUB		5
69
70/*
71 * Note: due to the setup of the newfs code, the current state of the program
72 * and its options are held in a few global variables. The FS specific parts
73 * are in global `context' and 'layout' structures.
74 */
75
76/* global variables describing disc and format requests */
77int	 req_enable, req_disable;
78
79
80/* --------------------------------------------------------------------- */
81
82static int
83udf_readonly_format(void)
84{
85	/*
86	 * we choose the emulated profile to determine this since the media
87	 * might be different from the format we create. Say creating a CDROM
88	 * on a CD-R media.
89	 */
90	switch (emul_mmc_profile) {
91	case 0x00:	/* unknown, treat as CDROM */
92	case 0x08:	/* CDROM */
93	case 0x10:	/* DVDROM */
94	case 0x40:	/* BDROM */
95		return true;
96	}
97	return false;
98}
99
100
101#define OPT_STR(letter, name, desc)  \
102	{ letter, name, NULL, OPT_STRBUF, 0, 0, desc }
103
104#define OPT_NUM(letter, name, field, min, max, desc) \
105	{ letter, name, &context.field, \
106	  sizeof(context.field) == 8 ? OPT_INT64 : \
107	  (sizeof(context.field) == 4 ? OPT_INT32 : \
108	  (sizeof(context.field) == 2 ? OPT_INT16 : OPT_INT8)), \
109	  min, max, desc }
110
111#define OPT_BOOL(letter, name, field, desc) \
112	OPT_NUM(letter, name, field, 0, 1, desc)
113
114void
115udf_prep_opts(fsinfo_t *fsopts)
116{
117	const option_t udf_options[] = {
118		OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,bdrom,"
119			"dvdram,bdre,disk,cdr,dvdr,bdr,cdrw,dvdrw)"),
120		OPT_STR('L', "loglabel", "\"logical volume name\""),
121		OPT_STR('P', "discid",   "\"[volset name ':']"
122			"physical volume name\""),
123		OPT_NUM('t', "tz", gmtoff, -24, 24, "timezone"),
124		OPT_STR('v', "minver", "minimum UDF version in either "
125			"``0x201'' or ``2.01'' format"),
126		OPT_STR('V', "maxver", "maximum UDF version in either "
127			"``0x201'' or ``2.01'' format"),
128		OPT_NUM('p', "metaperc", meta_perc, 1, 99,
129			"minimum free metadata percentage"),
130		OPT_BOOL('c', "checksurface", check_surface,
131			"perform crude surface check on rewritable media"),
132		OPT_BOOL('F', "forceformat", create_new_session,
133			"force file system construction on non-empty recordable media"),
134		{ .name = NULL }
135	};
136
137	/* initialise */
138	req_enable = req_disable = 0;
139	fsopts->sectorsize = 512;	/* minimum allowed sector size */
140
141	srandom((unsigned long) time(NULL));
142
143	udf_init_create_context();
144	context.app_name         = "*NetBSD UDF";
145	context.app_version_main = APP_VERSION_MAIN;
146	context.app_version_sub  = APP_VERSION_SUB;
147	context.impl_name        = IMPL_NAME;
148
149	/* minimum and maximum UDF versions we advise */
150	context.min_udf = 0x102;
151	context.max_udf = 0x250;	/* 0x260 is not ready */
152
153	/* defaults for disc/files */
154	emul_mmc_profile  =  -1;	/* invalid->no emulation	*/
155	emul_packetsize   =   1;	/* reasonable default		*/
156	emul_sectorsize   = 512;	/* minimum allowed sector size	*/
157	emul_size	  =   0;	/* empty			*/
158
159	/* use user's time zone as default */
160#ifdef HAVE_STRUCT_TM_TM_GMTOFF
161	if (!stampst.st_ino)  {
162		struct tm tm;
163		time_t now;
164		(void)time(&now);
165		(void)localtime_r(&now, &tm);
166		context.gmtoff = tm.tm_gmtoff;
167	} else
168#endif
169		context.gmtoff = 0;
170
171	/* return info */
172	fsopts->fs_specific = NULL;
173	fsopts->fs_options = copy_opts(udf_options);
174}
175
176
177void
178udf_cleanup_opts(fsinfo_t *fsopts)
179{
180	free(fsopts->fs_options);
181}
182
183
184/* ----- included from newfs_udf.c ------ */
185
186#define CDRSIZE    ((uint64_t)   700*1024*1024)	/* small approx */
187#define CDRWSIZE   ((uint64_t)   576*1024*1024)	/* small approx */
188#define DVDRSIZE   ((uint64_t)  4488*1024*1024)	/* small approx */
189#define DVDRAMSIZE ((uint64_t)  4330*1024*1024)	/* small approx with spare */
190#define DVDRWSIZE  ((uint64_t)  4482*1024*1024)	/* small approx */
191#define BDRSIZE    ((uint64_t) 23866*1024*1024)	/* small approx */
192#define BDRESIZE   ((uint64_t) 23098*1024*1024)	/* small approx */
193int
194udf_parse_opts(const char *option, fsinfo_t *fsopts)
195{
196	option_t *udf_options = fsopts->fs_options;
197	uint64_t stdsize, maxsize;
198	uint32_t set_sectorsize;
199	char buffer[1024], *buf, *colon;
200	int i;
201
202	assert(option != NULL);
203
204	if (debug & DEBUG_FS_PARSE_OPTS)
205		printf("udf_parse_opts: got `%s'\n", option);
206
207	i = set_option(udf_options, option, buffer, sizeof(buffer));
208	if (i == -1)
209		return 0;
210
211	if (udf_options[i].name == NULL)
212		abort();
213
214	set_sectorsize = 0;
215	stdsize = 0;
216	maxsize = 0;
217
218	buf = buffer;
219	switch (udf_options[i].letter) {
220	case 'T':
221		if (strcmp(buf, "cdrom") == 0) {
222			emul_mmc_profile = 0x00;
223			maxsize = CDRSIZE;
224		} else if (strcmp(buf, "dvdrom") == 0) {
225			emul_mmc_profile = 0x10;
226			maxsize = DVDRSIZE;
227		} else if (strcmp(buf, "bdrom") == 0) {
228			emul_mmc_profile = 0x40;
229			maxsize = BDRSIZE;
230		} else if (strcmp(buf, "dvdram") == 0) {
231			emul_mmc_profile = 0x12;
232			stdsize = DVDRAMSIZE;
233		} else if (strcmp(buf, "bdre") == 0) {
234			emul_mmc_profile = 0x43;
235			stdsize = BDRESIZE;
236		} else if (strcmp(buf, "disk") == 0) {
237			emul_mmc_profile = 0x01;
238		} else if (strcmp(buf, "cdr") == 0) {
239			emul_mmc_profile = 0x09;
240			stdsize = CDRSIZE;
241		} else if (strcmp(buf, "dvdr") == 0) {
242			emul_mmc_profile = 0x1b;
243			stdsize = DVDRSIZE;
244		} else if (strcmp(buf, "bdr") == 0) {
245			emul_mmc_profile = 0x41;
246			stdsize = BDRSIZE;
247		} else if (strcmp(buf, "cdrw") == 0) {
248			emul_mmc_profile = 0x0a;
249			stdsize = CDRWSIZE;
250		} else if (strcmp(buf, "dvdrw") == 0) {
251			emul_mmc_profile = 0x1a;
252			stdsize = DVDRWSIZE;
253		} else {
254			errx(1, "unknown or unimplemented disc format");
255		}
256		if (emul_mmc_profile != 0x01)
257			set_sectorsize = 2048;
258		break;
259	case 'L':
260		if (context.logvol_name) free(context.logvol_name);
261		context.logvol_name = strdup(buf);
262		break;
263	case 'P':
264		if ((colon = strstr(buf, ":"))) {
265			if (context.volset_name)
266				free(context.volset_name);
267			*colon = 0;
268			context.volset_name = strdup(buf);
269			buf = colon+1;
270		}
271		if (context.primary_name)
272			free(context.primary_name);
273		if ((strstr(buf, ":")))
274			errx(1, "primary name can't have ':' in its name");
275		context.primary_name = strdup(buf);
276		break;
277	case 'v':
278		context.min_udf = a_udf_version(buf, "min_udf");
279		if (context.min_udf > 0x250)
280			errx(1, "maximum supported version is UDF 2.50");
281		if (context.min_udf > context.max_udf)
282			context.max_udf = context.min_udf;
283		break;
284	case 'V':
285		context.max_udf = a_udf_version(buf, "min_udf");
286		if (context.max_udf > 0x250)
287			errx(1, "maximum supported version is UDF 2.50");
288		if (context.min_udf > context.max_udf)
289			context.min_udf = context.max_udf;
290		break;
291	}
292	if (set_sectorsize)
293		fsopts->sectorsize = set_sectorsize;
294	if (stdsize) {
295		if (fsopts->maxsize > 0)
296			stdsize = MIN(stdsize, (uint64_t) fsopts->maxsize);
297		if (fsopts->minsize > 0)
298			stdsize = MAX(stdsize, (uint64_t) fsopts->minsize);
299		fsopts->size = fsopts->minsize = fsopts->maxsize = stdsize;
300	}
301	if (maxsize) {
302		if (fsopts->maxsize > 0)
303			maxsize = MIN(maxsize, (uint64_t) fsopts->maxsize);
304		if (fsopts->minsize > 0)
305			maxsize = MAX(maxsize, (uint64_t) fsopts->minsize);
306		fsopts->maxsize = maxsize;
307	}
308	return 1;
309}
310
311/* -
312 * -------------------------------------------------------------------- */
313
314struct udf_stats {
315	uint32_t nfiles;
316	uint32_t ndirs;
317	uint32_t ndescr;
318	uint32_t nmetadatablocks;
319	uint32_t ndatablocks;
320};
321
322
323/* node reference administration */
324static void
325udf_inc_link(union dscrptr *dscr)
326{
327	struct file_entry *fe;
328	struct extfile_entry *efe;
329
330	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
331		fe        = &dscr->fe;
332		fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1);
333	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
334		efe       = &dscr->efe;
335		efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1);
336	} else {
337		errx(1, "bad tag passed to udf_inc_link");
338	}
339}
340
341
342static void
343udf_set_link_cnt(union dscrptr *dscr, int num)
344{
345	struct file_entry *fe;
346	struct extfile_entry *efe;
347
348	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
349		fe        = &dscr->fe;
350		fe->link_cnt = udf_rw16(num);
351	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
352		efe       = &dscr->efe;
353		efe->link_cnt = udf_rw16(num);
354	} else {
355		errx(1, "bad tag passed to udf_set_link_cnt");
356	}
357}
358
359
360static uint32_t
361udf_datablocks(off_t sz)
362{
363	/* predictor if it can be written inside the node */
364	/* XXX the predictor assumes NO extended attributes in the node */
365	if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16)
366		return 0;
367
368	return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size;
369}
370
371
372static void
373udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb,
374		uint8_t *dirdata, uint32_t dirdata_size)
375{
376	struct fileid_desc *fid;
377	struct long_ad     *icb;
378	uint32_t fidsize, offset;
379	uint32_t location;
380
381	if (udf_datablocks(dirdata_size) == 0) {
382		/* going internal */
383		icb = dir_icb;
384	} else {
385		/* external blocks to write to */
386		icb = dirdata_icb;
387	}
388
389	for (offset = 0; offset < dirdata_size; offset += fidsize) {
390		/* for each FID: */
391		fid = (struct fileid_desc *) (dirdata + offset);
392		assert(udf_rw16(fid->tag.id) == TAGID_FID);
393
394		location  = udf_rw32(icb->loc.lb_num);
395		location += offset / context.sector_size;
396
397		fid->tag.tag_loc = udf_rw32(location);
398		udf_validate_tag_and_crc_sums((union dscrptr *) fid);
399
400		fidsize = udf_fidsize(fid);
401	}
402}
403
404
405static int
406udf_file_inject_blob(union dscrptr *dscr,  uint8_t *blob, off_t size)
407{
408	struct icb_tag *icb;
409	struct file_entry *fe;
410	struct extfile_entry *efe;
411	uint64_t inf_len, obj_size;
412	uint32_t l_ea, l_ad;
413	uint16_t crclen;
414	uint8_t *data, *pos;
415
416	fe = NULL;
417	efe = NULL;
418	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
419		fe        = &dscr->fe;
420		data      = fe->data;
421		l_ea      = udf_rw32(fe->l_ea);
422		l_ad      = udf_rw32(fe->l_ad);
423		icb       = &fe->icbtag;
424		inf_len   = udf_rw64(fe->inf_len);
425		obj_size  = 0;
426	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
427		efe       = &dscr->efe;
428		data      = efe->data;
429		l_ea      = udf_rw32(efe->l_ea);
430		l_ad      = udf_rw32(efe->l_ad);
431		icb       = &efe->icbtag;
432		inf_len   = udf_rw64(efe->inf_len);
433		obj_size  = udf_rw64(efe->obj_size);
434	} else {
435		errx(1, "bad tag passed to udf_file_inject_blob");
436	}
437	crclen = udf_rw16(dscr->tag.desc_crc_len);
438
439	/* check if we can go internal */
440	if ((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) !=
441			UDF_ICB_INTERN_ALLOC)
442		return 1;
443
444	/* check if it will fit internally */
445	if (udf_datablocks(size)) {
446		/* the predictor tells it won't fit internally */
447		return 1;
448	}
449
450	/* going internal */
451	assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) ==
452			UDF_ICB_INTERN_ALLOC);
453	assert(l_ad == 0);
454
455	pos = data + l_ea + l_ad;
456	memcpy(pos, blob, size);
457	l_ad   += size;
458	crclen += size;
459
460	inf_len  += size;
461	obj_size += size;
462
463	if (fe) {
464		fe->l_ad = udf_rw32(l_ad);
465		fe->inf_len = udf_rw64(inf_len);
466	} else if (efe) {
467		efe->l_ad = udf_rw32(l_ad);
468		efe->inf_len  = udf_rw64(inf_len);
469		efe->obj_size = udf_rw64(inf_len);
470	}
471
472	/* make sure the header sums stays correct */
473	dscr->tag.desc_crc_len = udf_rw16(crclen);
474	udf_validate_tag_and_crc_sums(dscr);
475
476	return 0;
477}
478
479
480/* XXX no sparse file support */
481static void
482udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece)
483{
484	struct icb_tag *icb;
485	struct file_entry *fe;
486	struct extfile_entry *efe;
487	struct long_ad *last_long, last_piece;
488	struct short_ad *last_short, new_short;
489	uint64_t inf_len, obj_size, logblks_rec;
490	uint32_t l_ea, l_ad, size;
491	uint32_t last_lb_num, piece_lb_num;
492	uint64_t last_len, piece_len, last_flags;
493	uint64_t rest_len, merge_len, last_end;
494	uint16_t last_part_num, piece_part_num;
495	uint16_t crclen, cur_alloc;
496	uint8_t *data, *pos;
497	const int short_len = sizeof(struct short_ad);
498	const int long_len  = sizeof(struct long_ad);
499	const int sector_size = context.sector_size;
500	uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size);
501	int use_shorts;
502
503	fe  = NULL;
504	efe = NULL;
505	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
506		fe          = &dscr->fe;
507		data        = fe->data;
508		l_ea        = udf_rw32(fe->l_ea);
509		l_ad        = udf_rw32(fe->l_ad);
510		icb         = &fe->icbtag;
511		inf_len     = udf_rw64(fe->inf_len);
512		logblks_rec = udf_rw64(fe->logblks_rec);
513		obj_size = 0;
514	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
515		efe         = &dscr->efe;
516		data        = efe->data;
517		l_ea        = udf_rw32(efe->l_ea);
518		l_ad        = udf_rw32(efe->l_ad);
519		icb         = &efe->icbtag;
520		inf_len     = udf_rw64(efe->inf_len);
521		obj_size    = udf_rw64(efe->obj_size);
522		logblks_rec = udf_rw64(efe->logblks_rec);
523	} else {
524		errx(1, "bad tag passed to udf_file_append_blob");
525	}
526	crclen = udf_rw16(dscr->tag.desc_crc_len);
527
528	/* we use shorts if referring inside the metadata partition */
529	use_shorts = (udf_rw16(piece->loc.part_num) == context.metadata_part);
530
531	pos = data + l_ea;
532	cur_alloc = udf_rw16(icb->flags);
533	size = UDF_EXT_LEN(udf_rw32(piece->len));
534
535	/* extract last entry as a long_ad */
536	memset(&last_piece, 0, sizeof(last_piece));
537	last_len      = 0;
538	last_lb_num   = 0;
539	last_part_num = 0;
540	last_flags    = 0;
541	last_short    = NULL;
542	last_long     = NULL;
543	if (l_ad != 0) {
544		if (use_shorts) {
545			assert(cur_alloc == UDF_ICB_SHORT_ALLOC);
546			pos += l_ad - short_len;
547			last_short   = (struct short_ad *) pos;
548			last_lb_num  = udf_rw32(last_short->lb_num);
549			last_part_num = udf_rw16(piece->loc.part_num);
550			last_len     = UDF_EXT_LEN(udf_rw32(last_short->len));
551			last_flags   = UDF_EXT_FLAGS(udf_rw32(last_short->len));
552		} else {
553			assert(cur_alloc == UDF_ICB_LONG_ALLOC);
554			pos += l_ad - long_len;
555			last_long    = (struct long_ad *) pos;
556			last_lb_num  = udf_rw32(last_long->loc.lb_num);
557			last_part_num = udf_rw16(last_long->loc.part_num);
558			last_len     = UDF_EXT_LEN(udf_rw32(last_long->len));
559			last_flags   = UDF_EXT_FLAGS(udf_rw32(last_long->len));
560		}
561	}
562
563	piece_len      = UDF_EXT_LEN(udf_rw32(piece->len));
564	piece_lb_num   = udf_rw32(piece->loc.lb_num);
565	piece_part_num = udf_rw16(piece->loc.part_num);
566
567	/* try merging */
568	rest_len  = max_len - last_len;
569
570	merge_len = MIN(piece_len, rest_len);
571	last_end  = last_lb_num + (last_len / sector_size);
572	if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) {
573		/* we can merge */
574		last_len  += merge_len;
575		piece_len -= merge_len;
576
577		/* write back merge result */
578		if (use_shorts) {
579			last_short->len = udf_rw32(last_len | last_flags);
580		} else {
581			last_long->len  = udf_rw32(last_len | last_flags);
582		}
583		piece_lb_num += merge_len / sector_size;
584	}
585
586	if (piece_len) {
587		/* append new entry */
588		pos = data + l_ea + l_ad;
589		if (use_shorts) {
590			icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC);
591			memset(&new_short, 0, short_len);
592			new_short.len    = udf_rw32(piece_len);
593			new_short.lb_num = udf_rw32(piece_lb_num);
594			memcpy(pos, &new_short, short_len);
595			l_ad += short_len;
596			crclen += short_len;
597		} else {
598			icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC);
599			piece->len        = udf_rw32(piece_len);
600			piece->loc.lb_num = udf_rw32(piece_lb_num);
601			memcpy(pos, piece, long_len);
602			l_ad += long_len;
603			crclen += long_len;
604		}
605	}
606	piece->len = udf_rw32(0);
607
608	inf_len  += size;
609	obj_size += size;
610	logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size;
611
612	dscr->tag.desc_crc_len = udf_rw16(crclen);
613	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
614		fe->l_ad = udf_rw32(l_ad);
615		fe->inf_len = udf_rw64(inf_len);
616		fe->logblks_rec = udf_rw64(logblks_rec);
617	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
618		efe->l_ad = udf_rw32(l_ad);
619		efe->inf_len  = udf_rw64(inf_len);
620		efe->obj_size = udf_rw64(inf_len);
621		efe->logblks_rec = udf_rw64(logblks_rec);
622	}
623}
624
625
626static int
627udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb,
628		uint8_t *fdata, off_t flen)
629{
630	struct long_ad icb;
631	uint32_t location;
632	uint16_t vpart;
633	int sectors;
634
635	if (udf_file_inject_blob(dscr, fdata, flen) == 0)
636		return 0;
637
638	/* has to be appended in mappings */
639	icb = *data_icb;
640	icb.len = udf_rw32(flen);
641	while (udf_rw32(icb.len) > 0)
642		udf_append_file_mapping(dscr, &icb);
643	udf_validate_tag_and_crc_sums(dscr);
644
645	/* write out data piece */
646	vpart    = udf_rw16(data_icb->loc.part_num);
647	location = udf_rw32(data_icb->loc.lb_num);
648	sectors  = udf_datablocks(flen);
649
650	return udf_write_virt(fdata, location, vpart, sectors);
651}
652
653
654static int
655udf_create_new_file(struct stat *st, union dscrptr **dscr,
656	int filetype, struct long_ad *icb)
657{
658	struct file_entry *fe;
659	struct extfile_entry *efe;
660	int error;
661
662	fe = NULL;
663	efe = NULL;
664	if (context.dscrver == 2) {
665		error = udf_create_new_fe(&fe, filetype, st);
666		if (error)
667			errx(error, "can't create fe");
668		*dscr = (union dscrptr *) fe;
669		icb->longad_uniqueid = fe->unique_id;
670	} else {
671		error = udf_create_new_efe(&efe, filetype, st);
672		if (error)
673			errx(error, "can't create fe");
674		*dscr = (union dscrptr *) efe;
675		icb->longad_uniqueid = efe->unique_id;
676	}
677
678	return 0;
679}
680
681
682static void
683udf_estimate_walk(fsinfo_t *fsopts,
684		fsnode *root, char *dir, struct udf_stats *stats)
685{
686	struct fileid_desc *fid;
687	struct long_ad dummy_ref;
688	fsnode *cur;
689	fsinode *fnode;
690	size_t pathlen = strlen(dir);
691	char *mydir = dir + pathlen;
692	off_t sz;
693	uint32_t nblk, ddoff;
694	uint32_t softlink_len;
695	uint8_t *softlink_buf;
696	int nentries;
697	int error;
698
699	stats->ndirs++;
700
701	/*
702	 * Count number of directory entries and count directory size; needed
703	 * for the reservation of enough space for the directory. Pity we
704	 * don't keep the FIDs we created. If it turns out to be a issue we
705	 * can cache it later.
706	 */
707	fid = (struct fileid_desc *) malloc(context.sector_size);
708	assert(fid);
709
710	ddoff = 40;	/* '..' entry */
711	for (cur = root, nentries = 0; cur != NULL; cur = cur->next) {
712		switch (cur->type & S_IFMT) {
713		default:
714			/* what kind of nodes? */
715			break;
716		case S_IFCHR:
717		case S_IFBLK:
718			/* not supported yet */
719			break;
720		case S_IFDIR:
721			if (strcmp(cur->name, ".") == 0)
722				continue;
723			/* FALLTHROUGH */
724		case S_IFLNK:
725		case S_IFREG:
726			/* create dummy FID to see how long name will become */
727			memset(&dummy_ref, 0, sizeof(dummy_ref));
728			udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref);
729			nentries++;
730			ddoff += udf_fidsize(fid);
731			break;
732		}
733	}
734	sz = ddoff;
735
736	root->inode->st.st_size = sz;	/* max now */
737	root->inode->flags |= FI_SIZED;
738
739	nblk = udf_datablocks(sz);
740	stats->nmetadatablocks += nblk;
741
742	/* for each entry in the directory, there needs to be a (E)FE */
743	stats->nmetadatablocks += nentries + 1;
744
745	/* recurse */
746	for (cur = root; cur != NULL; cur = cur->next) {
747		switch (cur->type & S_IFMT) {
748		default:
749			/* what kind of nodes? */
750			break;
751		case S_IFDIR:
752			if (strcmp(cur->name, ".") == 0)
753				continue;
754			/* empty dir? */
755			if (!cur->child)
756				break;
757			mydir[0] = '/';
758			strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
759			udf_estimate_walk(fsopts, cur->child, dir, stats);
760			mydir[0] = '\0';
761			break;
762		case S_IFCHR:
763		case S_IFBLK:
764			/* not supported yet */
765			// stats->nfiles++;
766			break;
767		case S_IFREG:
768			fnode = cur->inode;
769			/* don't double-count hard-links */
770			if (!(fnode->flags & FI_SIZED)) {
771				sz = fnode->st.st_size;
772				nblk = udf_datablocks(sz);
773				stats->ndatablocks += nblk;
774				/* ... */
775				fnode->flags |= FI_SIZED;
776			}
777			stats->nfiles++;
778			break;
779		case S_IFLNK:
780			/* softlink */
781			fnode = cur->inode;
782			/* don't double-count hard-links */
783			if (!(fnode->flags & FI_SIZED)) {
784				error = udf_encode_symlink(&softlink_buf,
785						&softlink_len, cur->symlink);
786				if (error) {
787					printf("SOFTLINK error %d\n", error);
788					break;
789				}
790				nblk = udf_datablocks(softlink_len);
791				stats->ndatablocks += nblk;
792				fnode->flags |= FI_SIZED;
793
794				free(softlink_buf);
795			}
796			stats->nfiles++;
797			break;
798		}
799	}
800}
801
802
803#define UDF_MAX_CHUNK_SIZE (4*1024*1024)
804static int
805udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid,
806	struct long_ad *icb)
807{
808	union dscrptr *dscr;
809	struct long_ad data_icb;
810	fsinode *fnode;
811	off_t sz, chunk, rd;
812	uint8_t *data;
813	bool intern;
814	int nblk;
815	int f;
816	int error;
817
818	fnode = cur->inode;
819
820	f = open(path, O_RDONLY);
821	if (f < 0) {
822		warn("Can't open file %s for reading", cur->name);
823		return errno;
824	}
825
826	/* claim disc space for the (e)fe descriptor for this file */
827	udf_metadata_alloc(1, icb);
828	udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb);
829
830	sz = fnode->st.st_size;
831
832	chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
833	data = malloc(MAX(chunk, context.sector_size));
834	assert(data);
835
836	intern = (udf_datablocks(chunk) == 0);
837	error = 0;
838	while (chunk) {
839		rd = read(f, data, chunk);
840		if (rd != chunk) {
841			warn("Short read of file %s", cur->name);
842			error = errno;
843			break;
844		}
845
846		nblk = UDF_ROUNDUP(chunk, context.sector_size) / context.sector_size;
847		if (chunk && !intern)
848			udf_data_alloc(nblk, &data_icb);
849		udf_append_file_contents(dscr, &data_icb, data, chunk);
850
851		sz -= chunk;
852		chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
853	}
854	close(f);
855	free(data);
856
857	/* write out dscr (e)fe */
858	udf_set_link_cnt(dscr, fnode->nlink);
859	udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num),
860		udf_rw16(icb->loc.part_num), 1);
861	free(dscr);
862
863	/* remember our location for hardlinks */
864	cur->inode->fsuse = malloc(sizeof(struct long_ad));
865	memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad));
866
867	return error;
868}
869
870
871static int
872udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir,
873		struct long_ad *parent_icb, struct long_ad *dir_icb)
874{
875	union dscrptr *dir_dscr, *dscr;
876	struct fileid_desc *fid;
877	struct long_ad icb, data_icb, dirdata_icb;
878	fsnode *cur;
879	fsinode *fnode;
880	size_t pathlen = strlen(dir);
881	size_t dirlen;
882	char *mydir = dir + pathlen;
883	uint32_t nblk, ddoff;
884	uint32_t softlink_len;
885	uint8_t *softlink_buf;
886	uint8_t *dirdata;
887	int error, ret, retval;
888
889	/* claim disc space for the (e)fe descriptor for this dir */
890	udf_metadata_alloc(1, dir_icb);
891
892	/* create new e(fe) */
893	udf_create_new_file(&root->inode->st, &dir_dscr,
894		UDF_ICB_FILETYPE_DIRECTORY, dir_icb);
895
896	/* allocate memory for the directory contents */
897	dirlen = root->inode->st.st_size;
898	nblk = UDF_ROUNDUP(dirlen, context.sector_size) / context.sector_size;
899	dirdata = malloc(nblk * context.sector_size);
900	assert(dirdata);
901	memset(dirdata, 0, nblk * context.sector_size);
902
903	/* create and append '..' */
904	fid = (struct fileid_desc *) dirdata;
905	ddoff = udf_create_parentfid(fid, parent_icb);
906	assert(ddoff == 40);
907
908	/* for '..' */
909	udf_inc_link(dir_dscr);
910
911	/* recurse */
912	retval = 0;
913	for (cur = root; cur != NULL; cur = cur->next) {
914		mydir[0] = '/';
915		strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
916
917		fid = (struct fileid_desc *) (dirdata + ddoff);
918		switch (cur->type & S_IFMT) {
919		default:
920			/* what kind of nodes? */
921			retval = 2;
922			break;
923		case S_IFCHR:
924		case S_IFBLK:
925			/* not supported */
926			retval = 2;
927			warnx("device node %s not supported", dir);
928			break;
929		case S_IFDIR:
930			/* not an empty dir? */
931			if (strcmp(cur->name, ".") == 0)
932				break;
933			assert(cur->child);
934			if (cur->child) {
935				ret = udf_populate_walk(fsopts, cur->child,
936					dir, dir_icb, &icb);
937				if (ret)
938					retval = 2;
939			}
940			udf_create_fid(ddoff, fid, cur->name,
941					UDF_FILE_CHAR_DIR, &icb);
942			udf_inc_link(dir_dscr);
943			ddoff += udf_fidsize(fid);
944			break;
945		case S_IFREG:
946			fnode = cur->inode;
947			/* don't re-copy hard-links */
948			if (!(fnode->flags & FI_WRITTEN)) {
949				printf("%s\n", dir);
950				error = udf_copy_file(&fnode->st, dir, cur,
951					fid, &icb);
952				if (!error) {
953					fnode->flags |= FI_WRITTEN;
954					udf_create_fid(ddoff, fid, cur->name,
955						0, &icb);
956					ddoff += udf_fidsize(fid);
957				} else {
958					retval = 2;
959				}
960			} else {
961				/* hardlink! */
962				printf("%s (hardlink)\n", dir);
963				udf_create_fid(ddoff, fid, cur->name,
964					0, (struct long_ad *) (fnode->fsuse));
965				ddoff += udf_fidsize(fid);
966			}
967			fnode->nlink--;
968			if (fnode->nlink == 0)
969				free(fnode->fsuse);
970			break;
971		case S_IFLNK:
972			/* softlink */
973			fnode = cur->inode;
974			printf("%s -> %s\n", dir, cur->symlink);
975			error = udf_encode_symlink(&softlink_buf,
976					&softlink_len, cur->symlink);
977			if (error) {
978				printf("SOFTLINK error %d\n", error);
979				retval = 2;
980				break;
981			}
982
983			udf_metadata_alloc(1, &icb);
984			udf_create_new_file(&fnode->st, &dscr,
985				UDF_ICB_FILETYPE_SYMLINK, &icb);
986
987			nblk = udf_datablocks(softlink_len);
988			if (nblk > 0)
989				udf_data_alloc(nblk, &data_icb);
990			udf_append_file_contents(dscr, &data_icb,
991					softlink_buf, softlink_len);
992
993			/* write out dscr (e)fe */
994			udf_inc_link(dscr);
995			udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num),
996				udf_rw16(icb.loc.part_num), 1);
997
998			free(dscr);
999			free(softlink_buf);
1000
1001			udf_create_fid(ddoff, fid, cur->name, 0, &icb);
1002			ddoff += udf_fidsize(fid);
1003			break;
1004		}
1005		mydir[0] = '\0';
1006	}
1007	assert(dirlen == ddoff);
1008
1009	/* pre allocate space for the directory contents */
1010	memset(&dirdata_icb, 0, sizeof(dirdata_icb));
1011	nblk = udf_datablocks(dirlen);
1012
1013	/* claim disc space for the dir contents if needed */
1014	if (nblk > 0)
1015		udf_fids_alloc(nblk, &dirdata_icb);
1016
1017	udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen);
1018	udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen);
1019
1020	/* write out dir_dscr (e)fe */
1021	udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num),
1022			udf_rw16(dir_icb->loc.part_num), 1);
1023
1024	free(dirdata);
1025	free(dir_dscr);
1026	return retval;
1027}
1028
1029
1030static int
1031udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts,
1032		struct udf_stats *stats)
1033{
1034	struct long_ad rooticb;
1035	static char path[MAXPATHLEN+1];
1036	int error;
1037
1038	strncpy(path, dir, sizeof(path));
1039	error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb);
1040
1041	return error;
1042}
1043
1044
1045static void
1046udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts,
1047		struct udf_stats *stats)
1048{
1049	char path[MAXPATHLEN + 1];
1050	off_t proposed_size;
1051	uint32_t n, nblk, nmetablk, nbytes;
1052	uint32_t spareable_blocks, spareable_blockingnr;
1053
1054	strncpy(path, dir, sizeof(path));
1055
1056	/* calculate strict minimal size */
1057	udf_estimate_walk(fsopts, root, path, stats);
1058#if 0
1059	printf("ndirs            %d\n", stats->ndirs);
1060	printf("nfiles           %d\n", stats->nfiles);
1061	printf("ndata_blocks     %d\n", stats->ndatablocks);
1062	printf("nmetadata_blocks %d\n", stats->nmetadatablocks);
1063	printf("\n");
1064#endif
1065
1066	/* adjust for options : free file nodes */
1067	if (fsopts->freefiles) {
1068		/* be mercifull and reserve more for the FID */
1069		stats->nmetadatablocks += fsopts->freefiles * 1.5;
1070	} else if ((n = fsopts->freefilepc)) {
1071		stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n);
1072	}
1073
1074	/* adjust for options : free data blocks */
1075	if (fsopts->freeblocks) {
1076		stats->ndatablocks += fsopts->freeblocks;
1077	} else if ((n = fsopts->freeblockpc)) {
1078		stats->ndatablocks += (stats->ndatablocks * n) / (100-n);
1079	}
1080
1081	/* rough predictor of minimum disc size */
1082	nblk  = stats->ndatablocks + stats->nmetadatablocks;
1083	if (context.format_flags & FORMAT_META) {
1084		float meta_p;
1085		double factor;
1086
1087		meta_p = (float) context.meta_perc/100.0;
1088		factor = meta_p / (1.0 - meta_p);
1089
1090		/* add space for metadata partition including some slack */
1091		nmetablk = factor * nblk + 32;
1092		nblk =  stats->ndatablocks + nmetablk;
1093
1094		/* free space maps */
1095		nbytes = ceil((double) nblk * (1.0/8.0));
1096		nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size;
1097		if (!(context.format_flags & FORMAT_READONLY)) {
1098			nbytes = ceil((double) nmetablk * (1.0/8.0));
1099			nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size;
1100		}
1101	} else if (context.format_flags & FORMAT_SEQUENTIAL) {
1102		/* nothing */
1103	} else {
1104		if (!(context.format_flags & FORMAT_READONLY)) {
1105			nbytes = ceil((double) nblk * (1.0/8.0));
1106			nblk += 1 + (nbytes + context.sector_size-1)/
1107				context.sector_size;
1108		}
1109	}
1110
1111	/*
1112	 * Make extra room for spareable table if requested
1113	 */
1114	if (context.format_flags & FORMAT_SPAREABLE) {
1115		spareable_blockingnr = udf_spareable_blockingnr();
1116		spareable_blocks     = udf_spareable_blocks();
1117
1118		nblk += spareable_blocks * spareable_blockingnr;
1119		nblk += spareable_blockingnr;		/* slack */
1120	}
1121
1122	nblk += 256;					/* pre-volume space  */
1123	nblk += 256;					/* post-volume space */
1124	nblk += 1024;					/* safeguard	     */
1125
1126	/* try to honour minimum size */
1127	n = fsopts->minsize / fsopts->sectorsize;
1128	if (nblk < n) {
1129		stats->ndatablocks += (n - nblk);
1130		nblk += n - nblk;
1131	}
1132	proposed_size = (off_t) nblk * fsopts->sectorsize;
1133	/* sanity size */
1134	if (proposed_size < 512*1024)
1135		proposed_size = 512*1024;
1136
1137	if (fsopts->size) {
1138		if (fsopts->size < proposed_size)
1139			errx(1, "makefs_udf: won't fit on disc!");
1140	} else {
1141		fsopts->size = proposed_size;
1142	}
1143
1144	fsopts->inodes = stats->nfiles + stats->ndirs;
1145}
1146
1147
1148void
1149udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
1150{
1151	struct udf_stats stats;
1152	uint64_t truncate_len;
1153	uint32_t last_sector, ext;
1154	char scrap[255];
1155	int error;
1156
1157	/* setup */
1158	emul_sectorsize = fsopts->sectorsize;
1159	emul_size = 0;
1160	context.sector_size = fsopts->sectorsize;
1161
1162	/* names */
1163	error = udf_proces_names();
1164	if (error)
1165		errx(1, "bad names given");
1166
1167	/* open disc device or emulated file */
1168	if (udf_opendisc(image, O_CREAT)) {
1169		udf_closedisc();
1170		errx(1, "can't open %s", image);
1171	}
1172	fsopts->fd = dev_fd;
1173
1174	/* determine format */
1175	if (udf_readonly_format())
1176		req_enable |= FORMAT_READONLY;
1177	// printf("req_enable %d, req_disable %d\n", req_enable, req_disable);
1178	error = udf_derive_format(req_enable, req_disable);
1179	if (error) {
1180		udf_closedisc();
1181		errx(1, "can't derive format from media/settings");
1182	}
1183
1184	/* estimate the amount of space needed */
1185	memset(&stats, 0, sizeof(stats));
1186	udf_enumerate_and_estimate(dir, root, fsopts, &stats);
1187
1188	printf("Calculated size of `%s' is "
1189		"%"PRIu64" KiB, %"PRIu64" MiB, %"PRIu64" GiB with %ld inodes\n",
1190		image,
1191		(uint64_t) fsopts->size/1024,
1192		(uint64_t) fsopts->size/1024/1024,
1193		(uint64_t) fsopts->size/1024/1024/1024,
1194		(long)fsopts->inodes);
1195	emul_size = MAX(emul_size, fsopts->size);
1196	if ((fsopts->maxsize > 0) && (emul_size > fsopts->maxsize))
1197		errx(1, "won't fit due to set maximum disk size");
1198
1199	/* prepare disc if necessary (recordables mainly) */
1200	error = udf_prepare_disc();
1201	if (error) {
1202		udf_closedisc();
1203		errx(1, "preparing disc failed");
1204	}
1205
1206	/* update mmc info but now with correct size */
1207	udf_update_discinfo();
1208	udf_dump_discinfo(&mmc_discinfo);
1209
1210	printf("Building disc compatible with UDF version %x to %x\n\n",
1211		context.min_udf, context.max_udf);
1212	(void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS,
1213	    (uint64_t) context.format_flags);
1214	printf("UDF properties       %s\n", scrap);
1215	printf("Volume set          `%s'\n", context.volset_name);
1216	printf("Primary volume      `%s`\n", context.primary_name);
1217	printf("Logical volume      `%s`\n", context.logvol_name);
1218	if (context.format_flags & FORMAT_META)
1219		printf("Metadata percentage  %d%% (%d%% used)\n",
1220			context.meta_perc,
1221			(int) ceil(100.0*stats.nmetadatablocks/stats.ndatablocks));
1222	printf("\n");
1223
1224	/* prefix */
1225	udf_allow_writing();
1226	if (udf_do_newfs_prefix()) {
1227		udf_closedisc();
1228		errx(1, "basic setup failed");
1229	}
1230
1231	/* update context */
1232	context.unique_id = 0;
1233
1234	/* add all directories */
1235	error = udf_populate(dir, root, fsopts, &stats);
1236
1237	if (!error) {
1238		/* update values for integrity sequence */
1239		context.num_files = stats.nfiles;
1240		context.num_directories = stats.ndirs;
1241
1242		udf_do_newfs_postfix();
1243
1244		if (S_ISREG(dev_fd_stat.st_mode) &&
1245				(context.format_flags & FORMAT_VAT)) {
1246			udf_translate_vtop(context.alloc_pos[context.data_part],
1247				context.data_part,
1248				&last_sector, &ext);
1249			truncate_len = (uint64_t) last_sector * context.sector_size;
1250
1251			printf("\nTruncing the disc-image to allow for VAT\n");
1252			printf("Free space left on this volume approx. "
1253				"%"PRIu64" KiB, %"PRIu64" MiB\n",
1254				(fsopts->size - truncate_len)/1024,
1255				(fsopts->size - truncate_len)/1024/1024);
1256			ftruncate(dev_fd, truncate_len);
1257		}
1258	}
1259	udf_closedisc();
1260
1261	if (error == 2)
1262		errx(error, "not all files could be added");
1263	if (error == 1)
1264		errx(error, "creation of %s failed", image);
1265	return;
1266}
1267