xref: /netbsd-src/usr.sbin/makefs/udf.c (revision ec5d1d82264719b6458ad15f7bffd992eca8419f)
1*ec5d1d82Stsutsui /* $NetBSD: udf.c,v 1.31 2023/12/28 12:13:55 tsutsui Exp $ */
2e2036ad8Sreinoud 
3e2036ad8Sreinoud /*
48f4e1cd9Sreinoud  * Copyright (c) 2006, 2008, 2013, 2021, 2022 Reinoud Zandijk
5e2036ad8Sreinoud  * All rights reserved.
6e2036ad8Sreinoud  *
7e2036ad8Sreinoud  * Redistribution and use in source and binary forms, with or without
8e2036ad8Sreinoud  * modification, are permitted provided that the following conditions
9e2036ad8Sreinoud  * are met:
10e2036ad8Sreinoud  * 1. Redistributions of source code must retain the above copyright
11e2036ad8Sreinoud  *    notice, this list of conditions and the following disclaimer.
12e2036ad8Sreinoud  * 2. Redistributions in binary form must reproduce the above copyright
13e2036ad8Sreinoud  *    notice, this list of conditions and the following disclaimer in the
14e2036ad8Sreinoud  *    documentation and/or other materials provided with the distribution.
15e2036ad8Sreinoud  *
16e2036ad8Sreinoud  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e2036ad8Sreinoud  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18e2036ad8Sreinoud  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19e2036ad8Sreinoud  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20e2036ad8Sreinoud  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21e2036ad8Sreinoud  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22e2036ad8Sreinoud  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23e2036ad8Sreinoud  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24e2036ad8Sreinoud  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25e2036ad8Sreinoud  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26e2036ad8Sreinoud  *
27e2036ad8Sreinoud  */
289656a3f6Sjoerg #if HAVE_NBTOOL_CONFIG_H
299656a3f6Sjoerg #include "nbtool_config.h"
309656a3f6Sjoerg #endif
31e2036ad8Sreinoud 
32e2036ad8Sreinoud #include <sys/cdefs.h>
33*ec5d1d82Stsutsui __RCSID("$NetBSD: udf.c,v 1.31 2023/12/28 12:13:55 tsutsui Exp $");
34e2036ad8Sreinoud 
35e2036ad8Sreinoud #include <stdio.h>
36e2036ad8Sreinoud #include <stdlib.h>
37e2036ad8Sreinoud #include <string.h>
38e2036ad8Sreinoud #include <errno.h>
39e2036ad8Sreinoud #include <time.h>
40e2036ad8Sreinoud #include <assert.h>
41e2036ad8Sreinoud #include <err.h>
42e2036ad8Sreinoud #include <unistd.h>
43e2036ad8Sreinoud #include <fcntl.h>
448f4e1cd9Sreinoud #include <math.h>
45e2036ad8Sreinoud #include <sys/types.h>
46e2036ad8Sreinoud #include <sys/param.h>
47e2036ad8Sreinoud #include <sys/stat.h>
48e2036ad8Sreinoud #include <util.h>
49e2036ad8Sreinoud 
502b17bd9aSreinoud #if !HAVE_NBTOOL_CONFIG_H
512b17bd9aSreinoud #define _EXPOSE_MMC
522b17bd9aSreinoud #include <sys/cdio.h>
532b17bd9aSreinoud #else
542b17bd9aSreinoud #include "udf/cdio_mmc_structs.h"
552b17bd9aSreinoud #endif
562b17bd9aSreinoud 
5736f0ae60Sjmcneill #if !HAVE_NBTOOL_CONFIG_H
5836f0ae60Sjmcneill #define HAVE_STRUCT_TM_TM_GMTOFF
5936f0ae60Sjmcneill #endif
6036f0ae60Sjmcneill 
61e2036ad8Sreinoud #include "makefs.h"
628f4e1cd9Sreinoud #include "udf_core.h"
63e2036ad8Sreinoud #include "newfs_udf.h"
64e2036ad8Sreinoud 
658f4e1cd9Sreinoud /* identification */
668f4e1cd9Sreinoud #define IMPL_NAME		"*NetBSD makefs 10.0"
678f4e1cd9Sreinoud #define APP_VERSION_MAIN	0
688f4e1cd9Sreinoud #define APP_VERSION_SUB		5
69e2036ad8Sreinoud 
70e2036ad8Sreinoud /*
71e2036ad8Sreinoud  * Note: due to the setup of the newfs code, the current state of the program
7216e91b5fSandvar  * and its options are held in a few global variables. The FS specific parts
738f4e1cd9Sreinoud  * are in global `context' and 'layout' structures.
74e2036ad8Sreinoud  */
75e2036ad8Sreinoud 
76e2036ad8Sreinoud /* global variables describing disc and format requests */
77e2036ad8Sreinoud int	 req_enable, req_disable;
78e2036ad8Sreinoud 
79e2036ad8Sreinoud 
80e2036ad8Sreinoud /* --------------------------------------------------------------------- */
81e2036ad8Sreinoud 
82e2036ad8Sreinoud static int
udf_readonly_format(void)838f4e1cd9Sreinoud udf_readonly_format(void)
84e2036ad8Sreinoud {
858f4e1cd9Sreinoud 	/*
868f4e1cd9Sreinoud 	 * we choose the emulated profile to determine this since the media
878f4e1cd9Sreinoud 	 * might be different from the format we create. Say creating a CDROM
888f4e1cd9Sreinoud 	 * on a CD-R media.
898f4e1cd9Sreinoud 	 */
908f4e1cd9Sreinoud 	switch (emul_mmc_profile) {
91e2036ad8Sreinoud 	case 0x00:	/* unknown, treat as CDROM */
92e2036ad8Sreinoud 	case 0x08:	/* CDROM */
93f7be5947Sreinoud 	case 0x10:	/* DVDROM */
94f7be5947Sreinoud 	case 0x40:	/* BDROM */
958f4e1cd9Sreinoud 		return true;
96e2036ad8Sreinoud 	}
978f4e1cd9Sreinoud 	return false;
98e2036ad8Sreinoud }
99e2036ad8Sreinoud 
100e2036ad8Sreinoud 
101e2036ad8Sreinoud #define OPT_STR(letter, name, desc)  \
102e2036ad8Sreinoud 	{ letter, name, NULL, OPT_STRBUF, 0, 0, desc }
103e2036ad8Sreinoud 
104e2036ad8Sreinoud #define OPT_NUM(letter, name, field, min, max, desc) \
105c55e926fSreinoud 	{ letter, name, &context.field, \
106c55e926fSreinoud 	  sizeof(context.field) == 8 ? OPT_INT64 : \
107c55e926fSreinoud 	  (sizeof(context.field) == 4 ? OPT_INT32 : \
108c55e926fSreinoud 	  (sizeof(context.field) == 2 ? OPT_INT16 : OPT_INT8)), \
109e2036ad8Sreinoud 	  min, max, desc }
110e2036ad8Sreinoud 
111e2036ad8Sreinoud #define OPT_BOOL(letter, name, field, desc) \
112e2036ad8Sreinoud 	OPT_NUM(letter, name, field, 0, 1, desc)
113e2036ad8Sreinoud 
114e2036ad8Sreinoud void
udf_prep_opts(fsinfo_t * fsopts)115e2036ad8Sreinoud udf_prep_opts(fsinfo_t *fsopts)
116e2036ad8Sreinoud {
117e2036ad8Sreinoud 	const option_t udf_options[] = {
118a1f170f5Sreinoud 		OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,bdrom,"
119a1f170f5Sreinoud 			"dvdram,bdre,disk,cdr,dvdr,bdr,cdrw,dvdrw)"),
120a1f170f5Sreinoud 		OPT_STR('L', "loglabel", "\"logical volume name\""),
121c55e926fSreinoud 		OPT_STR('P', "discid",   "\"[volset name ':']"
122c55e926fSreinoud 			"physical volume name\""),
123c55e926fSreinoud 		OPT_NUM('t', "tz", gmtoff, -24, 24, "timezone"),
124c55e926fSreinoud 		OPT_STR('v', "minver", "minimum UDF version in either "
125c55e926fSreinoud 			"``0x201'' or ``2.01'' format"),
126c55e926fSreinoud 		OPT_STR('V', "maxver", "maximum UDF version in either "
127c55e926fSreinoud 			"``0x201'' or ``2.01'' format"),
1288f4e1cd9Sreinoud 		OPT_NUM('p', "metaperc", meta_perc, 1, 99,
1298f4e1cd9Sreinoud 			"minimum free metadata percentage"),
1308f4e1cd9Sreinoud 		OPT_BOOL('c', "checksurface", check_surface,
1318f4e1cd9Sreinoud 			"perform crude surface check on rewritable media"),
1328f4e1cd9Sreinoud 		OPT_BOOL('F', "forceformat", create_new_session,
133374cb9beSwiz 			"force file system construction on non-empty recordable media"),
134e2036ad8Sreinoud 		{ .name = NULL }
135e2036ad8Sreinoud 	};
136e2036ad8Sreinoud 
137e2036ad8Sreinoud 	/* initialise */
138e2036ad8Sreinoud 	req_enable = req_disable = 0;
139e2036ad8Sreinoud 	fsopts->sectorsize = 512;	/* minimum allowed sector size */
140e2036ad8Sreinoud 
141e2036ad8Sreinoud 	srandom((unsigned long) time(NULL));
142e2036ad8Sreinoud 
143e2036ad8Sreinoud 	udf_init_create_context();
1448f4e1cd9Sreinoud 	context.app_name         = "*NetBSD UDF";
145e2036ad8Sreinoud 	context.app_version_main = APP_VERSION_MAIN;
146e2036ad8Sreinoud 	context.app_version_sub  = APP_VERSION_SUB;
147254934d2Sreinoud 	context.impl_name        = IMPL_NAME;
148e2036ad8Sreinoud 
149e2036ad8Sreinoud 	/* minimum and maximum UDF versions we advise */
150e2036ad8Sreinoud 	context.min_udf = 0x102;
1518f4e1cd9Sreinoud 	context.max_udf = 0x250;	/* 0x260 is not ready */
1528f4e1cd9Sreinoud 
1538f4e1cd9Sreinoud 	/* defaults for disc/files */
1548f4e1cd9Sreinoud 	emul_mmc_profile  =  -1;	/* invalid->no emulation	*/
1558f4e1cd9Sreinoud 	emul_packetsize   =   1;	/* reasonable default		*/
1568f4e1cd9Sreinoud 	emul_sectorsize   = 512;	/* minimum allowed sector size	*/
1578f4e1cd9Sreinoud 	emul_size	  =   0;	/* empty			*/
158e2036ad8Sreinoud 
159e2036ad8Sreinoud 	/* use user's time zone as default */
16036f0ae60Sjmcneill #ifdef HAVE_STRUCT_TM_TM_GMTOFF
161683c28c5Schristos 	if (!stampst.st_ino)  {
162683c28c5Schristos 		struct tm tm;
163683c28c5Schristos 		time_t now;
164683c28c5Schristos 		(void)time(&now);
165683c28c5Schristos 		(void)localtime_r(&now, &tm);
166683c28c5Schristos 		context.gmtoff = tm.tm_gmtoff;
167683c28c5Schristos 	} else
16836f0ae60Sjmcneill #endif
169683c28c5Schristos 		context.gmtoff = 0;
170e2036ad8Sreinoud 
171e2036ad8Sreinoud 	/* return info */
172e2036ad8Sreinoud 	fsopts->fs_specific = NULL;
173e2036ad8Sreinoud 	fsopts->fs_options = copy_opts(udf_options);
174e2036ad8Sreinoud }
175e2036ad8Sreinoud 
176e2036ad8Sreinoud 
177e2036ad8Sreinoud void
udf_cleanup_opts(fsinfo_t * fsopts)178e2036ad8Sreinoud udf_cleanup_opts(fsinfo_t *fsopts)
179e2036ad8Sreinoud {
180e2036ad8Sreinoud 	free(fsopts->fs_options);
181e2036ad8Sreinoud }
182e2036ad8Sreinoud 
183e2036ad8Sreinoud 
184c55e926fSreinoud /* ----- included from newfs_udf.c ------ */
185c55e926fSreinoud 
186f7be5947Sreinoud #define CDRSIZE    ((uint64_t)   700*1024*1024)	/* small approx */
187f7be5947Sreinoud #define CDRWSIZE   ((uint64_t)   576*1024*1024)	/* small approx */
188f7be5947Sreinoud #define DVDRSIZE   ((uint64_t)  4488*1024*1024)	/* small approx */
189f7be5947Sreinoud #define DVDRAMSIZE ((uint64_t)  4330*1024*1024)	/* small approx with spare */
190f7be5947Sreinoud #define DVDRWSIZE  ((uint64_t)  4482*1024*1024)	/* small approx */
191f7be5947Sreinoud #define BDRSIZE    ((uint64_t) 23866*1024*1024)	/* small approx */
192f7be5947Sreinoud #define BDRESIZE   ((uint64_t) 23098*1024*1024)	/* small approx */
193e2036ad8Sreinoud int
udf_parse_opts(const char * option,fsinfo_t * fsopts)194e2036ad8Sreinoud udf_parse_opts(const char *option, fsinfo_t *fsopts)
195e2036ad8Sreinoud {
196e2036ad8Sreinoud 	option_t *udf_options = fsopts->fs_options;
1978f4e1cd9Sreinoud 	uint64_t stdsize, maxsize;
198f7be5947Sreinoud 	uint32_t set_sectorsize;
199a1f170f5Sreinoud 	char buffer[1024], *buf, *colon;
200e2036ad8Sreinoud 	int i;
201e2036ad8Sreinoud 
202e2036ad8Sreinoud 	assert(option != NULL);
203e2036ad8Sreinoud 
204e2036ad8Sreinoud 	if (debug & DEBUG_FS_PARSE_OPTS)
205e2036ad8Sreinoud 		printf("udf_parse_opts: got `%s'\n", option);
206e2036ad8Sreinoud 
207a1f170f5Sreinoud 	i = set_option(udf_options, option, buffer, sizeof(buffer));
208e2036ad8Sreinoud 	if (i == -1)
209e2036ad8Sreinoud 		return 0;
210e2036ad8Sreinoud 
211e2036ad8Sreinoud 	if (udf_options[i].name == NULL)
212e2036ad8Sreinoud 		abort();
213e2036ad8Sreinoud 
214f7be5947Sreinoud 	set_sectorsize = 0;
215f7be5947Sreinoud 	stdsize = 0;
2168f4e1cd9Sreinoud 	maxsize = 0;
217f7be5947Sreinoud 
218a1f170f5Sreinoud 	buf = buffer;
219e2036ad8Sreinoud 	switch (udf_options[i].letter) {
220e2036ad8Sreinoud 	case 'T':
221e2036ad8Sreinoud 		if (strcmp(buf, "cdrom") == 0) {
2228f4e1cd9Sreinoud 			emul_mmc_profile = 0x00;
2238f4e1cd9Sreinoud 			maxsize = CDRSIZE;
224e2036ad8Sreinoud 		} else if (strcmp(buf, "dvdrom") == 0) {
2258f4e1cd9Sreinoud 			emul_mmc_profile = 0x10;
2268f4e1cd9Sreinoud 			maxsize = DVDRSIZE;
227f7be5947Sreinoud 		} else if (strcmp(buf, "bdrom") == 0) {
2288f4e1cd9Sreinoud 			emul_mmc_profile = 0x40;
2298f4e1cd9Sreinoud 			maxsize = BDRSIZE;
230e2036ad8Sreinoud 		} else if (strcmp(buf, "dvdram") == 0) {
2318f4e1cd9Sreinoud 			emul_mmc_profile = 0x12;
232f7be5947Sreinoud 			stdsize = DVDRAMSIZE;
233e2036ad8Sreinoud 		} else if (strcmp(buf, "bdre") == 0) {
2348f4e1cd9Sreinoud 			emul_mmc_profile = 0x43;
235f7be5947Sreinoud 			stdsize = BDRESIZE;
236e2036ad8Sreinoud 		} else if (strcmp(buf, "disk") == 0) {
2378f4e1cd9Sreinoud 			emul_mmc_profile = 0x01;
238e2036ad8Sreinoud 		} else if (strcmp(buf, "cdr") == 0) {
2398f4e1cd9Sreinoud 			emul_mmc_profile = 0x09;
240f7be5947Sreinoud 			stdsize = CDRSIZE;
241e2036ad8Sreinoud 		} else if (strcmp(buf, "dvdr") == 0) {
2428f4e1cd9Sreinoud 			emul_mmc_profile = 0x1b;
243f7be5947Sreinoud 			stdsize = DVDRSIZE;
244f7be5947Sreinoud 		} else if (strcmp(buf, "bdr") == 0) {
2458f4e1cd9Sreinoud 			emul_mmc_profile = 0x41;
246f7be5947Sreinoud 			stdsize = BDRSIZE;
247e2036ad8Sreinoud 		} else if (strcmp(buf, "cdrw") == 0) {
2488f4e1cd9Sreinoud 			emul_mmc_profile = 0x0a;
249f7be5947Sreinoud 			stdsize = CDRWSIZE;
250e2036ad8Sreinoud 		} else if (strcmp(buf, "dvdrw") == 0) {
2518f4e1cd9Sreinoud 			emul_mmc_profile = 0x1a;
252f7be5947Sreinoud 			stdsize = DVDRWSIZE;
253e2036ad8Sreinoud 		} else {
254*ec5d1d82Stsutsui 			errx(EXIT_FAILURE,
255*ec5d1d82Stsutsui 			    "unknown or unimplemented disc format");
256e2036ad8Sreinoud 		}
2578f4e1cd9Sreinoud 		if (emul_mmc_profile != 0x01)
258f7be5947Sreinoud 			set_sectorsize = 2048;
259a1f170f5Sreinoud 		break;
260a1f170f5Sreinoud 	case 'L':
261a1f170f5Sreinoud 		if (context.logvol_name) free(context.logvol_name);
262a1f170f5Sreinoud 		context.logvol_name = strdup(buf);
263a1f170f5Sreinoud 		break;
264a1f170f5Sreinoud 	case 'P':
265a1f170f5Sreinoud 		if ((colon = strstr(buf, ":"))) {
266a1f170f5Sreinoud 			if (context.volset_name)
267a1f170f5Sreinoud 				free(context.volset_name);
268a1f170f5Sreinoud 			*colon = 0;
269a1f170f5Sreinoud 			context.volset_name = strdup(buf);
270a1f170f5Sreinoud 			buf = colon+1;
271a1f170f5Sreinoud 		}
272a1f170f5Sreinoud 		if (context.primary_name)
273a1f170f5Sreinoud 			free(context.primary_name);
2748f4e1cd9Sreinoud 		if ((strstr(buf, ":")))
275*ec5d1d82Stsutsui 			errx(EXIT_FAILURE,
276*ec5d1d82Stsutsui 			    "primary name can't have ':' in its name");
277a1f170f5Sreinoud 		context.primary_name = strdup(buf);
278a1f170f5Sreinoud 		break;
279c55e926fSreinoud 	case 'v':
280c55e926fSreinoud 		context.min_udf = a_udf_version(buf, "min_udf");
2818f4e1cd9Sreinoud 		if (context.min_udf > 0x250)
282*ec5d1d82Stsutsui 			errx(EXIT_FAILURE,
283*ec5d1d82Stsutsui 			    "maximum supported version is UDF 2.50");
284c55e926fSreinoud 		if (context.min_udf > context.max_udf)
285c55e926fSreinoud 			context.max_udf = context.min_udf;
286c55e926fSreinoud 		break;
2878f4e1cd9Sreinoud 	case 'V':
2888f4e1cd9Sreinoud 		context.max_udf = a_udf_version(buf, "min_udf");
2898f4e1cd9Sreinoud 		if (context.max_udf > 0x250)
290*ec5d1d82Stsutsui 			errx(EXIT_FAILURE,
291*ec5d1d82Stsutsui 			    "maximum supported version is UDF 2.50");
2928f4e1cd9Sreinoud 		if (context.min_udf > context.max_udf)
2938f4e1cd9Sreinoud 			context.min_udf = context.max_udf;
2948f4e1cd9Sreinoud 		break;
295e2036ad8Sreinoud 	}
296f7be5947Sreinoud 	if (set_sectorsize)
297f7be5947Sreinoud 		fsopts->sectorsize = set_sectorsize;
2988f4e1cd9Sreinoud 	if (stdsize) {
2998f4e1cd9Sreinoud 		if (fsopts->maxsize > 0)
3008f4e1cd9Sreinoud 			stdsize = MIN(stdsize, (uint64_t) fsopts->maxsize);
3018f4e1cd9Sreinoud 		if (fsopts->minsize > 0)
3028f4e1cd9Sreinoud 			stdsize = MAX(stdsize, (uint64_t) fsopts->minsize);
3038f4e1cd9Sreinoud 		fsopts->size = fsopts->minsize = fsopts->maxsize = stdsize;
3048f4e1cd9Sreinoud 	}
3058f4e1cd9Sreinoud 	if (maxsize) {
3068f4e1cd9Sreinoud 		if (fsopts->maxsize > 0)
3078f4e1cd9Sreinoud 			maxsize = MIN(maxsize, (uint64_t) fsopts->maxsize);
3088f4e1cd9Sreinoud 		if (fsopts->minsize > 0)
3098f4e1cd9Sreinoud 			maxsize = MAX(maxsize, (uint64_t) fsopts->minsize);
3108f4e1cd9Sreinoud 		fsopts->maxsize = maxsize;
3118f4e1cd9Sreinoud 	}
312e2036ad8Sreinoud 	return 1;
313e2036ad8Sreinoud }
314e2036ad8Sreinoud 
3158f4e1cd9Sreinoud /* -
3168f4e1cd9Sreinoud  * -------------------------------------------------------------------- */
317e2036ad8Sreinoud 
318e2036ad8Sreinoud struct udf_stats {
319e2036ad8Sreinoud 	uint32_t nfiles;
320e2036ad8Sreinoud 	uint32_t ndirs;
321e2036ad8Sreinoud 	uint32_t ndescr;
322e2036ad8Sreinoud 	uint32_t nmetadatablocks;
323e2036ad8Sreinoud 	uint32_t ndatablocks;
324e2036ad8Sreinoud };
325e2036ad8Sreinoud 
326e2036ad8Sreinoud 
327e2036ad8Sreinoud /* node reference administration */
328e2036ad8Sreinoud static void
udf_inc_link(union dscrptr * dscr)329e2036ad8Sreinoud udf_inc_link(union dscrptr *dscr)
330e2036ad8Sreinoud {
331e2036ad8Sreinoud 	struct file_entry *fe;
332e2036ad8Sreinoud 	struct extfile_entry *efe;
333e2036ad8Sreinoud 
334e2036ad8Sreinoud 	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
335e2036ad8Sreinoud 		fe        = &dscr->fe;
336e2036ad8Sreinoud 		fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1);
337e2036ad8Sreinoud 	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
338e2036ad8Sreinoud 		efe       = &dscr->efe;
339e2036ad8Sreinoud 		efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1);
340e2036ad8Sreinoud 	} else {
341*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "bad tag passed to udf_inc_link");
342e2036ad8Sreinoud 	}
343e2036ad8Sreinoud }
344e2036ad8Sreinoud 
345e2036ad8Sreinoud 
346e2036ad8Sreinoud static void
udf_set_link_cnt(union dscrptr * dscr,int num)347e2036ad8Sreinoud udf_set_link_cnt(union dscrptr *dscr, int num)
348e2036ad8Sreinoud {
349e2036ad8Sreinoud 	struct file_entry *fe;
350e2036ad8Sreinoud 	struct extfile_entry *efe;
351e2036ad8Sreinoud 
352e2036ad8Sreinoud 	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
353e2036ad8Sreinoud 		fe        = &dscr->fe;
354e2036ad8Sreinoud 		fe->link_cnt = udf_rw16(num);
355e2036ad8Sreinoud 	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
356e2036ad8Sreinoud 		efe       = &dscr->efe;
357e2036ad8Sreinoud 		efe->link_cnt = udf_rw16(num);
358e2036ad8Sreinoud 	} else {
359*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "bad tag passed to udf_set_link_cnt");
360e2036ad8Sreinoud 	}
361e2036ad8Sreinoud }
362e2036ad8Sreinoud 
363e2036ad8Sreinoud 
364e2036ad8Sreinoud static uint32_t
udf_datablocks(off_t sz)365e2036ad8Sreinoud udf_datablocks(off_t sz)
366e2036ad8Sreinoud {
367e2036ad8Sreinoud 	/* predictor if it can be written inside the node */
36893d5858eSreinoud 	/* XXX the predictor assumes NO extended attributes in the node */
369e2036ad8Sreinoud 	if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16)
370e2036ad8Sreinoud 		return 0;
371e2036ad8Sreinoud 
372e2036ad8Sreinoud 	return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size;
373e2036ad8Sreinoud }
374e2036ad8Sreinoud 
375e2036ad8Sreinoud 
376e2036ad8Sreinoud static void
udf_prepare_fids(struct long_ad * dir_icb,struct long_ad * dirdata_icb,uint8_t * dirdata,uint32_t dirdata_size)377e2036ad8Sreinoud udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb,
378e2036ad8Sreinoud 		uint8_t *dirdata, uint32_t dirdata_size)
379e2036ad8Sreinoud {
380e2036ad8Sreinoud 	struct fileid_desc *fid;
381e2036ad8Sreinoud 	struct long_ad     *icb;
382e2036ad8Sreinoud 	uint32_t fidsize, offset;
383e2036ad8Sreinoud 	uint32_t location;
384e2036ad8Sreinoud 
385e2036ad8Sreinoud 	if (udf_datablocks(dirdata_size) == 0) {
386e2036ad8Sreinoud 		/* going internal */
387e2036ad8Sreinoud 		icb = dir_icb;
388e2036ad8Sreinoud 	} else {
389e2036ad8Sreinoud 		/* external blocks to write to */
390e2036ad8Sreinoud 		icb = dirdata_icb;
391e2036ad8Sreinoud 	}
392e2036ad8Sreinoud 
393e2036ad8Sreinoud 	for (offset = 0; offset < dirdata_size; offset += fidsize) {
394e2036ad8Sreinoud 		/* for each FID: */
395e2036ad8Sreinoud 		fid = (struct fileid_desc *) (dirdata + offset);
396e2036ad8Sreinoud 		assert(udf_rw16(fid->tag.id) == TAGID_FID);
397e2036ad8Sreinoud 
398e2036ad8Sreinoud 		location  = udf_rw32(icb->loc.lb_num);
399e2036ad8Sreinoud 		location += offset / context.sector_size;
400e2036ad8Sreinoud 
401e2036ad8Sreinoud 		fid->tag.tag_loc = udf_rw32(location);
402e2036ad8Sreinoud 		udf_validate_tag_and_crc_sums((union dscrptr *) fid);
403e2036ad8Sreinoud 
404e2036ad8Sreinoud 		fidsize = udf_fidsize(fid);
405e2036ad8Sreinoud 	}
406e2036ad8Sreinoud }
407e2036ad8Sreinoud 
4088f4e1cd9Sreinoud 
409e2036ad8Sreinoud static int
udf_file_inject_blob(union dscrptr * dscr,uint8_t * blob,off_t size)410a5f25cf5Sreinoud udf_file_inject_blob(union dscrptr *dscr,  uint8_t *blob, off_t size)
411e2036ad8Sreinoud {
412e2036ad8Sreinoud 	struct icb_tag *icb;
413e2036ad8Sreinoud 	struct file_entry *fe;
414e2036ad8Sreinoud 	struct extfile_entry *efe;
415e2036ad8Sreinoud 	uint64_t inf_len, obj_size;
416e2036ad8Sreinoud 	uint32_t l_ea, l_ad;
417e2036ad8Sreinoud 	uint16_t crclen;
418e2036ad8Sreinoud 	uint8_t *data, *pos;
419e2036ad8Sreinoud 
420e2036ad8Sreinoud 	fe = NULL;
421e2036ad8Sreinoud 	efe = NULL;
422e2036ad8Sreinoud 	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
423e2036ad8Sreinoud 		fe        = &dscr->fe;
424e2036ad8Sreinoud 		data      = fe->data;
425e2036ad8Sreinoud 		l_ea      = udf_rw32(fe->l_ea);
426e2036ad8Sreinoud 		l_ad      = udf_rw32(fe->l_ad);
427e2036ad8Sreinoud 		icb       = &fe->icbtag;
428e2036ad8Sreinoud 		inf_len   = udf_rw64(fe->inf_len);
429e2036ad8Sreinoud 		obj_size  = 0;
430e2036ad8Sreinoud 	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
431e2036ad8Sreinoud 		efe       = &dscr->efe;
432e2036ad8Sreinoud 		data      = efe->data;
433e2036ad8Sreinoud 		l_ea      = udf_rw32(efe->l_ea);
434e2036ad8Sreinoud 		l_ad      = udf_rw32(efe->l_ad);
435e2036ad8Sreinoud 		icb       = &efe->icbtag;
436e2036ad8Sreinoud 		inf_len   = udf_rw64(efe->inf_len);
437e2036ad8Sreinoud 		obj_size  = udf_rw64(efe->obj_size);
438e2036ad8Sreinoud 	} else {
439*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "bad tag passed to udf_file_inject_blob");
440e2036ad8Sreinoud 	}
441e2036ad8Sreinoud 	crclen = udf_rw16(dscr->tag.desc_crc_len);
442e2036ad8Sreinoud 
4438f4e1cd9Sreinoud 	/* check if we can go internal */
4448f4e1cd9Sreinoud 	if ((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) !=
4458f4e1cd9Sreinoud 			UDF_ICB_INTERN_ALLOC)
4468f4e1cd9Sreinoud 		return 1;
4478f4e1cd9Sreinoud 
44893d5858eSreinoud 	/* check if it will fit internally */
449e2036ad8Sreinoud 	if (udf_datablocks(size)) {
45093d5858eSreinoud 		/* the predictor tells it won't fit internally */
451e2036ad8Sreinoud 		return 1;
452e2036ad8Sreinoud 	}
453e2036ad8Sreinoud 
454e2036ad8Sreinoud 	/* going internal */
455ef2095d8Sreinoud 	assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) ==
456ef2095d8Sreinoud 			UDF_ICB_INTERN_ALLOC);
4578f4e1cd9Sreinoud 	assert(l_ad == 0);
458e2036ad8Sreinoud 
459e2036ad8Sreinoud 	pos = data + l_ea + l_ad;
460e2036ad8Sreinoud 	memcpy(pos, blob, size);
461e2036ad8Sreinoud 	l_ad   += size;
462e2036ad8Sreinoud 	crclen += size;
463e2036ad8Sreinoud 
464e2036ad8Sreinoud 	inf_len  += size;
465e2036ad8Sreinoud 	obj_size += size;
466e2036ad8Sreinoud 
467e2036ad8Sreinoud 	if (fe) {
468e2036ad8Sreinoud 		fe->l_ad = udf_rw32(l_ad);
469e2036ad8Sreinoud 		fe->inf_len = udf_rw64(inf_len);
470e2036ad8Sreinoud 	} else if (efe) {
471e2036ad8Sreinoud 		efe->l_ad = udf_rw32(l_ad);
472e2036ad8Sreinoud 		efe->inf_len  = udf_rw64(inf_len);
473e2036ad8Sreinoud 		efe->obj_size = udf_rw64(inf_len);
474e2036ad8Sreinoud 	}
475e2036ad8Sreinoud 
476e2036ad8Sreinoud 	/* make sure the header sums stays correct */
477e2036ad8Sreinoud 	dscr->tag.desc_crc_len = udf_rw16(crclen);
478e2036ad8Sreinoud 	udf_validate_tag_and_crc_sums(dscr);
479e2036ad8Sreinoud 
4805823a2adSreinoud 	(void) obj_size;
481e2036ad8Sreinoud 	return 0;
482e2036ad8Sreinoud }
483e2036ad8Sreinoud 
484e2036ad8Sreinoud 
485e2036ad8Sreinoud /* XXX no sparse file support */
486e2036ad8Sreinoud static void
udf_append_file_mapping(union dscrptr * dscr,struct long_ad * piece)487e2036ad8Sreinoud udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece)
488e2036ad8Sreinoud {
489e2036ad8Sreinoud 	struct icb_tag *icb;
490e2036ad8Sreinoud 	struct file_entry *fe;
491e2036ad8Sreinoud 	struct extfile_entry *efe;
492e2036ad8Sreinoud 	struct long_ad *last_long, last_piece;
493e2036ad8Sreinoud 	struct short_ad *last_short, new_short;
494e2036ad8Sreinoud 	uint64_t inf_len, obj_size, logblks_rec;
495e2036ad8Sreinoud 	uint32_t l_ea, l_ad, size;
496e2036ad8Sreinoud 	uint32_t last_lb_num, piece_lb_num;
497857cc1afSreinoud 	uint64_t last_len, piece_len, last_flags;
498857cc1afSreinoud 	uint64_t rest_len, merge_len, last_end;
499e2036ad8Sreinoud 	uint16_t last_part_num, piece_part_num;
500e2036ad8Sreinoud 	uint16_t crclen, cur_alloc;
501e2036ad8Sreinoud 	uint8_t *data, *pos;
502e2036ad8Sreinoud 	const int short_len = sizeof(struct short_ad);
503e2036ad8Sreinoud 	const int long_len  = sizeof(struct long_ad);
504e2036ad8Sreinoud 	const int sector_size = context.sector_size;
505e2036ad8Sreinoud 	uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size);
5068f4e1cd9Sreinoud 	int use_shorts;
507e2036ad8Sreinoud 
508a5f25cf5Sreinoud 	fe  = NULL;
509a5f25cf5Sreinoud 	efe = NULL;
510e2036ad8Sreinoud 	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
511e2036ad8Sreinoud 		fe          = &dscr->fe;
512e2036ad8Sreinoud 		data        = fe->data;
5131cb1a07cSreinoud 		l_ea        = udf_rw32(fe->l_ea);
514e2036ad8Sreinoud 		l_ad        = udf_rw32(fe->l_ad);
515e2036ad8Sreinoud 		icb         = &fe->icbtag;
516e2036ad8Sreinoud 		inf_len     = udf_rw64(fe->inf_len);
517e2036ad8Sreinoud 		logblks_rec = udf_rw64(fe->logblks_rec);
518e2036ad8Sreinoud 		obj_size = 0;
519e2036ad8Sreinoud 	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
520e2036ad8Sreinoud 		efe         = &dscr->efe;
521e2036ad8Sreinoud 		data        = efe->data;
5221cb1a07cSreinoud 		l_ea        = udf_rw32(efe->l_ea);
523e2036ad8Sreinoud 		l_ad        = udf_rw32(efe->l_ad);
524e2036ad8Sreinoud 		icb         = &efe->icbtag;
525e2036ad8Sreinoud 		inf_len     = udf_rw64(efe->inf_len);
526e2036ad8Sreinoud 		obj_size    = udf_rw64(efe->obj_size);
527e2036ad8Sreinoud 		logblks_rec = udf_rw64(efe->logblks_rec);
528e2036ad8Sreinoud 	} else {
529*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "bad tag passed to udf_file_append_blob");
530e2036ad8Sreinoud 	}
531e2036ad8Sreinoud 	crclen = udf_rw16(dscr->tag.desc_crc_len);
532e2036ad8Sreinoud 
5338f4e1cd9Sreinoud 	/* we use shorts if referring inside the metadata partition */
5348f4e1cd9Sreinoud 	use_shorts = (udf_rw16(piece->loc.part_num) == context.metadata_part);
5358f4e1cd9Sreinoud 
536e2036ad8Sreinoud 	pos = data + l_ea;
537e2036ad8Sreinoud 	cur_alloc = udf_rw16(icb->flags);
538e2036ad8Sreinoud 	size = UDF_EXT_LEN(udf_rw32(piece->len));
539e2036ad8Sreinoud 
540e2036ad8Sreinoud 	/* extract last entry as a long_ad */
54162e9bc7cSreinoud 	memset(&last_piece, 0, sizeof(last_piece));
54262e9bc7cSreinoud 	last_len      = 0;
54362e9bc7cSreinoud 	last_lb_num   = 0;
54462e9bc7cSreinoud 	last_part_num = 0;
545a5f25cf5Sreinoud 	last_flags    = 0;
546a5f25cf5Sreinoud 	last_short    = NULL;
547a5f25cf5Sreinoud 	last_long     = NULL;
548e2036ad8Sreinoud 	if (l_ad != 0) {
549e2036ad8Sreinoud 		if (use_shorts) {
550e2036ad8Sreinoud 			assert(cur_alloc == UDF_ICB_SHORT_ALLOC);
551e2036ad8Sreinoud 			pos += l_ad - short_len;
552e2036ad8Sreinoud 			last_short   = (struct short_ad *) pos;
553e2036ad8Sreinoud 			last_lb_num  = udf_rw32(last_short->lb_num);
554e2036ad8Sreinoud 			last_part_num = udf_rw16(piece->loc.part_num);
555e2036ad8Sreinoud 			last_len     = UDF_EXT_LEN(udf_rw32(last_short->len));
556e2036ad8Sreinoud 			last_flags   = UDF_EXT_FLAGS(udf_rw32(last_short->len));
557e2036ad8Sreinoud 		} else {
558e2036ad8Sreinoud 			assert(cur_alloc == UDF_ICB_LONG_ALLOC);
559e2036ad8Sreinoud 			pos += l_ad - long_len;
560e2036ad8Sreinoud 			last_long    = (struct long_ad *) pos;
561e2036ad8Sreinoud 			last_lb_num  = udf_rw32(last_long->loc.lb_num);
562e2036ad8Sreinoud 			last_part_num = udf_rw16(last_long->loc.part_num);
563e2036ad8Sreinoud 			last_len     = UDF_EXT_LEN(udf_rw32(last_long->len));
564e2036ad8Sreinoud 			last_flags   = UDF_EXT_FLAGS(udf_rw32(last_long->len));
565e2036ad8Sreinoud 		}
566e2036ad8Sreinoud 	}
567e2036ad8Sreinoud 
568e2036ad8Sreinoud 	piece_len      = UDF_EXT_LEN(udf_rw32(piece->len));
569e2036ad8Sreinoud 	piece_lb_num   = udf_rw32(piece->loc.lb_num);
570e2036ad8Sreinoud 	piece_part_num = udf_rw16(piece->loc.part_num);
571e2036ad8Sreinoud 
572e2036ad8Sreinoud 	/* try merging */
573e2036ad8Sreinoud 	rest_len  = max_len - last_len;
574e2036ad8Sreinoud 
575857cc1afSreinoud 	merge_len = MIN(piece_len, rest_len);
576e2036ad8Sreinoud 	last_end  = last_lb_num + (last_len / sector_size);
577e2036ad8Sreinoud 	if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) {
578e2036ad8Sreinoud 		/* we can merge */
579e2036ad8Sreinoud 		last_len  += merge_len;
580e2036ad8Sreinoud 		piece_len -= merge_len;
581e2036ad8Sreinoud 
582e2036ad8Sreinoud 		/* write back merge result */
583e2036ad8Sreinoud 		if (use_shorts) {
584e2036ad8Sreinoud 			last_short->len = udf_rw32(last_len | last_flags);
585e2036ad8Sreinoud 		} else {
586e2036ad8Sreinoud 			last_long->len  = udf_rw32(last_len | last_flags);
587e2036ad8Sreinoud 		}
588e2036ad8Sreinoud 		piece_lb_num += merge_len / sector_size;
589e2036ad8Sreinoud 	}
590e2036ad8Sreinoud 
591e2036ad8Sreinoud 	if (piece_len) {
592e2036ad8Sreinoud 		/* append new entry */
593e2036ad8Sreinoud 		pos = data + l_ea + l_ad;
594e2036ad8Sreinoud 		if (use_shorts) {
595e2036ad8Sreinoud 			icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC);
596e2036ad8Sreinoud 			memset(&new_short, 0, short_len);
597e2036ad8Sreinoud 			new_short.len    = udf_rw32(piece_len);
598e2036ad8Sreinoud 			new_short.lb_num = udf_rw32(piece_lb_num);
599e2036ad8Sreinoud 			memcpy(pos, &new_short, short_len);
600e2036ad8Sreinoud 			l_ad += short_len;
601e2036ad8Sreinoud 			crclen += short_len;
602e2036ad8Sreinoud 		} else {
603e2036ad8Sreinoud 			icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC);
604e2036ad8Sreinoud 			piece->len        = udf_rw32(piece_len);
605e2036ad8Sreinoud 			piece->loc.lb_num = udf_rw32(piece_lb_num);
606e2036ad8Sreinoud 			memcpy(pos, piece, long_len);
607e2036ad8Sreinoud 			l_ad += long_len;
608e2036ad8Sreinoud 			crclen += long_len;
609e2036ad8Sreinoud 		}
610e2036ad8Sreinoud 	}
611e2036ad8Sreinoud 	piece->len = udf_rw32(0);
612e2036ad8Sreinoud 
613e2036ad8Sreinoud 	inf_len  += size;
614e2036ad8Sreinoud 	obj_size += size;
615e2036ad8Sreinoud 	logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size;
616e2036ad8Sreinoud 
617e2036ad8Sreinoud 	dscr->tag.desc_crc_len = udf_rw16(crclen);
618e2036ad8Sreinoud 	if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) {
619e2036ad8Sreinoud 		fe->l_ad = udf_rw32(l_ad);
620e2036ad8Sreinoud 		fe->inf_len = udf_rw64(inf_len);
621e2036ad8Sreinoud 		fe->logblks_rec = udf_rw64(logblks_rec);
622e2036ad8Sreinoud 	} else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) {
623e2036ad8Sreinoud 		efe->l_ad = udf_rw32(l_ad);
624e2036ad8Sreinoud 		efe->inf_len  = udf_rw64(inf_len);
6255823a2adSreinoud 		efe->obj_size = udf_rw64(obj_size);
626e2036ad8Sreinoud 		efe->logblks_rec = udf_rw64(logblks_rec);
627e2036ad8Sreinoud 	}
628e2036ad8Sreinoud }
629e2036ad8Sreinoud 
630e2036ad8Sreinoud 
631e2036ad8Sreinoud static int
udf_append_file_contents(union dscrptr * dscr,struct long_ad * data_icb,uint8_t * fdata,off_t flen)632e2036ad8Sreinoud udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb,
633a5f25cf5Sreinoud 		uint8_t *fdata, off_t flen)
634e2036ad8Sreinoud {
635e2036ad8Sreinoud 	struct long_ad icb;
636e2036ad8Sreinoud 	uint32_t location;
637e2036ad8Sreinoud 	uint16_t vpart;
6388f4e1cd9Sreinoud 	int sectors;
639e2036ad8Sreinoud 
640e2036ad8Sreinoud 	if (udf_file_inject_blob(dscr, fdata, flen) == 0)
641e2036ad8Sreinoud 		return 0;
642e2036ad8Sreinoud 
643e2036ad8Sreinoud 	/* has to be appended in mappings */
644e2036ad8Sreinoud 	icb = *data_icb;
645e2036ad8Sreinoud 	icb.len = udf_rw32(flen);
646e2036ad8Sreinoud 	while (udf_rw32(icb.len) > 0)
647e2036ad8Sreinoud 		udf_append_file_mapping(dscr, &icb);
648e2036ad8Sreinoud 	udf_validate_tag_and_crc_sums(dscr);
649e2036ad8Sreinoud 
650e2036ad8Sreinoud 	/* write out data piece */
651e2036ad8Sreinoud 	vpart    = udf_rw16(data_icb->loc.part_num);
652e2036ad8Sreinoud 	location = udf_rw32(data_icb->loc.lb_num);
6538f4e1cd9Sreinoud 	sectors  = udf_datablocks(flen);
6548f4e1cd9Sreinoud 
6558f4e1cd9Sreinoud 	return udf_write_virt(fdata, location, vpart, sectors);
656e2036ad8Sreinoud }
657e2036ad8Sreinoud 
658e2036ad8Sreinoud 
659e2036ad8Sreinoud static int
udf_create_new_file(struct stat * st,union dscrptr ** dscr,int filetype,struct long_ad * icb)660e2036ad8Sreinoud udf_create_new_file(struct stat *st, union dscrptr **dscr,
661e2036ad8Sreinoud 	int filetype, struct long_ad *icb)
662e2036ad8Sreinoud {
663e2036ad8Sreinoud 	struct file_entry *fe;
664e2036ad8Sreinoud 	struct extfile_entry *efe;
665e2036ad8Sreinoud 	int error;
666e2036ad8Sreinoud 
667e2036ad8Sreinoud 	fe = NULL;
668e2036ad8Sreinoud 	efe = NULL;
669e2036ad8Sreinoud 	if (context.dscrver == 2) {
670e2036ad8Sreinoud 		error = udf_create_new_fe(&fe, filetype, st);
671e2036ad8Sreinoud 		if (error)
672e2036ad8Sreinoud 			errx(error, "can't create fe");
673e2036ad8Sreinoud 		*dscr = (union dscrptr *) fe;
674ecbc9e51Sreinoud 		icb->longad_uniqueid = udf_rw32(udf_rw64(fe->unique_id));
675e2036ad8Sreinoud 	} else {
676e2036ad8Sreinoud 		error = udf_create_new_efe(&efe, filetype, st);
677e2036ad8Sreinoud 		if (error)
678e2036ad8Sreinoud 			errx(error, "can't create fe");
679e2036ad8Sreinoud 		*dscr = (union dscrptr *) efe;
680ecbc9e51Sreinoud 		icb->longad_uniqueid = udf_rw32(udf_rw64(efe->unique_id));
681e2036ad8Sreinoud 	}
682e2036ad8Sreinoud 
683e2036ad8Sreinoud 	return 0;
684e2036ad8Sreinoud }
685e2036ad8Sreinoud 
686e2036ad8Sreinoud 
687e2036ad8Sreinoud static void
udf_estimate_walk(fsinfo_t * fsopts,fsnode * root,char * dir,struct udf_stats * stats)688e2036ad8Sreinoud udf_estimate_walk(fsinfo_t *fsopts,
689e2036ad8Sreinoud 		fsnode *root, char *dir, struct udf_stats *stats)
690e2036ad8Sreinoud {
691e2036ad8Sreinoud 	struct fileid_desc *fid;
6920850b0ccSjoerg 	struct long_ad dummy_ref;
693e2036ad8Sreinoud 	fsnode *cur;
694e2036ad8Sreinoud 	fsinode *fnode;
695e2036ad8Sreinoud 	size_t pathlen = strlen(dir);
696e2036ad8Sreinoud 	char *mydir = dir + pathlen;
697e2036ad8Sreinoud 	off_t sz;
698e2036ad8Sreinoud 	uint32_t nblk, ddoff;
699e2036ad8Sreinoud 	uint32_t softlink_len;
700e2036ad8Sreinoud 	uint8_t *softlink_buf;
701e2036ad8Sreinoud 	int nentries;
702e2036ad8Sreinoud 	int error;
703e2036ad8Sreinoud 
704e2036ad8Sreinoud 	stats->ndirs++;
705e2036ad8Sreinoud 
706e2036ad8Sreinoud 	/*
707e2036ad8Sreinoud 	 * Count number of directory entries and count directory size; needed
708e2036ad8Sreinoud 	 * for the reservation of enough space for the directory. Pity we
709e2036ad8Sreinoud 	 * don't keep the FIDs we created. If it turns out to be a issue we
710e2036ad8Sreinoud 	 * can cache it later.
711e2036ad8Sreinoud 	 */
712e2036ad8Sreinoud 	fid = (struct fileid_desc *) malloc(context.sector_size);
713e2036ad8Sreinoud 	assert(fid);
714e2036ad8Sreinoud 
715e2036ad8Sreinoud 	ddoff = 40;	/* '..' entry */
716e2036ad8Sreinoud 	for (cur = root, nentries = 0; cur != NULL; cur = cur->next) {
717e2036ad8Sreinoud 		switch (cur->type & S_IFMT) {
718e2036ad8Sreinoud 		default:
719e2036ad8Sreinoud 			/* what kind of nodes? */
720e2036ad8Sreinoud 			break;
721e2036ad8Sreinoud 		case S_IFCHR:
722e2036ad8Sreinoud 		case S_IFBLK:
723e2036ad8Sreinoud 			/* not supported yet */
7248f4e1cd9Sreinoud 			break;
725e2036ad8Sreinoud 		case S_IFDIR:
726e2036ad8Sreinoud 			if (strcmp(cur->name, ".") == 0)
727e2036ad8Sreinoud 				continue;
728fbffadb9Smrg 			/* FALLTHROUGH */
729e2036ad8Sreinoud 		case S_IFLNK:
730e2036ad8Sreinoud 		case S_IFREG:
731e2036ad8Sreinoud 			/* create dummy FID to see how long name will become */
7320850b0ccSjoerg 			memset(&dummy_ref, 0, sizeof(dummy_ref));
733e2036ad8Sreinoud 			udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref);
734e2036ad8Sreinoud 			nentries++;
735e2036ad8Sreinoud 			ddoff += udf_fidsize(fid);
7368f4e1cd9Sreinoud 			break;
737e2036ad8Sreinoud 		}
738e2036ad8Sreinoud 	}
739e2036ad8Sreinoud 	sz = ddoff;
740e2036ad8Sreinoud 
741e2036ad8Sreinoud 	root->inode->st.st_size = sz;	/* max now */
742e2036ad8Sreinoud 	root->inode->flags |= FI_SIZED;
743e2036ad8Sreinoud 
744e2036ad8Sreinoud 	nblk = udf_datablocks(sz);
745e2036ad8Sreinoud 	stats->nmetadatablocks += nblk;
746e2036ad8Sreinoud 
747e2036ad8Sreinoud 	/* for each entry in the directory, there needs to be a (E)FE */
748e2036ad8Sreinoud 	stats->nmetadatablocks += nentries + 1;
749e2036ad8Sreinoud 
750e2036ad8Sreinoud 	/* recurse */
751e2036ad8Sreinoud 	for (cur = root; cur != NULL; cur = cur->next) {
752e2036ad8Sreinoud 		switch (cur->type & S_IFMT) {
753e2036ad8Sreinoud 		default:
754e2036ad8Sreinoud 			/* what kind of nodes? */
755e2036ad8Sreinoud 			break;
756e2036ad8Sreinoud 		case S_IFDIR:
757e2036ad8Sreinoud 			if (strcmp(cur->name, ".") == 0)
758e2036ad8Sreinoud 				continue;
759e2036ad8Sreinoud 			/* empty dir? */
760e2036ad8Sreinoud 			if (!cur->child)
761e2036ad8Sreinoud 				break;
762e2036ad8Sreinoud 			mydir[0] = '/';
763e2036ad8Sreinoud 			strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
764e2036ad8Sreinoud 			udf_estimate_walk(fsopts, cur->child, dir, stats);
765e2036ad8Sreinoud 			mydir[0] = '\0';
766e2036ad8Sreinoud 			break;
7678f4e1cd9Sreinoud 		case S_IFCHR:
7688f4e1cd9Sreinoud 		case S_IFBLK:
7698f4e1cd9Sreinoud 			/* not supported yet */
7708f4e1cd9Sreinoud 			// stats->nfiles++;
7718f4e1cd9Sreinoud 			break;
772e2036ad8Sreinoud 		case S_IFREG:
773e2036ad8Sreinoud 			fnode = cur->inode;
774e2036ad8Sreinoud 			/* don't double-count hard-links */
775e2036ad8Sreinoud 			if (!(fnode->flags & FI_SIZED)) {
776e2036ad8Sreinoud 				sz = fnode->st.st_size;
777e2036ad8Sreinoud 				nblk = udf_datablocks(sz);
778e2036ad8Sreinoud 				stats->ndatablocks += nblk;
779e2036ad8Sreinoud 				/* ... */
780e2036ad8Sreinoud 				fnode->flags |= FI_SIZED;
781e2036ad8Sreinoud 			}
782e2036ad8Sreinoud 			stats->nfiles++;
783e2036ad8Sreinoud 			break;
784e2036ad8Sreinoud 		case S_IFLNK:
785e2036ad8Sreinoud 			/* softlink */
786e2036ad8Sreinoud 			fnode = cur->inode;
787e2036ad8Sreinoud 			/* don't double-count hard-links */
788e2036ad8Sreinoud 			if (!(fnode->flags & FI_SIZED)) {
789e2036ad8Sreinoud 				error = udf_encode_symlink(&softlink_buf,
790e2036ad8Sreinoud 						&softlink_len, cur->symlink);
791e2036ad8Sreinoud 				if (error) {
792e2036ad8Sreinoud 					printf("SOFTLINK error %d\n", error);
793e2036ad8Sreinoud 					break;
794e2036ad8Sreinoud 				}
795e2036ad8Sreinoud 				nblk = udf_datablocks(softlink_len);
796e2036ad8Sreinoud 				stats->ndatablocks += nblk;
797e2036ad8Sreinoud 				fnode->flags |= FI_SIZED;
798e2036ad8Sreinoud 
799e2036ad8Sreinoud 				free(softlink_buf);
800e2036ad8Sreinoud 			}
801e2036ad8Sreinoud 			stats->nfiles++;
802e2036ad8Sreinoud 			break;
803e2036ad8Sreinoud 		}
804e2036ad8Sreinoud 	}
805e2036ad8Sreinoud }
806e2036ad8Sreinoud 
807e2036ad8Sreinoud 
808e2036ad8Sreinoud #define UDF_MAX_CHUNK_SIZE (4*1024*1024)
809e2036ad8Sreinoud static int
udf_copy_file(struct stat * st,char * path,fsnode * cur,struct fileid_desc * fid,struct long_ad * icb)810e2036ad8Sreinoud udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid,
811e2036ad8Sreinoud 	struct long_ad *icb)
812e2036ad8Sreinoud {
813e2036ad8Sreinoud 	union dscrptr *dscr;
814e2036ad8Sreinoud 	struct long_ad data_icb;
815e2036ad8Sreinoud 	fsinode *fnode;
816a5f25cf5Sreinoud 	off_t sz, chunk, rd;
817e2036ad8Sreinoud 	uint8_t *data;
8188f4e1cd9Sreinoud 	bool intern;
819e2036ad8Sreinoud 	int nblk;
8208f4e1cd9Sreinoud 	int f;
8213551bde3Sreinoud 	int error;
822e2036ad8Sreinoud 
823e2036ad8Sreinoud 	fnode = cur->inode;
824e2036ad8Sreinoud 
8259656a3f6Sjoerg 	f = open(path, O_RDONLY);
826e2036ad8Sreinoud 	if (f < 0) {
827e2036ad8Sreinoud 		warn("Can't open file %s for reading", cur->name);
828e2036ad8Sreinoud 		return errno;
829e2036ad8Sreinoud 	}
830e2036ad8Sreinoud 
831e2036ad8Sreinoud 	/* claim disc space for the (e)fe descriptor for this file */
832e2036ad8Sreinoud 	udf_metadata_alloc(1, icb);
833e2036ad8Sreinoud 	udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb);
834e2036ad8Sreinoud 
835e2036ad8Sreinoud 	sz = fnode->st.st_size;
836e2036ad8Sreinoud 
837e2036ad8Sreinoud 	chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
838e2036ad8Sreinoud 	data = malloc(MAX(chunk, context.sector_size));
839e2036ad8Sreinoud 	assert(data);
840e2036ad8Sreinoud 
8418f4e1cd9Sreinoud 	intern = (udf_datablocks(chunk) == 0);
8423551bde3Sreinoud 	error = 0;
843e2036ad8Sreinoud 	while (chunk) {
844e2036ad8Sreinoud 		rd = read(f, data, chunk);
845e2036ad8Sreinoud 		if (rd != chunk) {
8468ee626c9Schristos 			warn("Short read of file %s", cur->name);
8473551bde3Sreinoud 			error = errno;
8483551bde3Sreinoud 			break;
849e2036ad8Sreinoud 		}
850e2036ad8Sreinoud 
8518f4e1cd9Sreinoud 		nblk = UDF_ROUNDUP(chunk, context.sector_size) / context.sector_size;
8528f4e1cd9Sreinoud 		if (chunk && !intern)
853e2036ad8Sreinoud 			udf_data_alloc(nblk, &data_icb);
854e2036ad8Sreinoud 		udf_append_file_contents(dscr, &data_icb, data, chunk);
855e2036ad8Sreinoud 
856e2036ad8Sreinoud 		sz -= chunk;
857e2036ad8Sreinoud 		chunk = MIN(sz, UDF_MAX_CHUNK_SIZE);
858e2036ad8Sreinoud 	}
859e2036ad8Sreinoud 	close(f);
860e2036ad8Sreinoud 	free(data);
861e2036ad8Sreinoud 
862e2036ad8Sreinoud 	/* write out dscr (e)fe */
863e2036ad8Sreinoud 	udf_set_link_cnt(dscr, fnode->nlink);
864e2036ad8Sreinoud 	udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num),
865e2036ad8Sreinoud 		udf_rw16(icb->loc.part_num), 1);
8663551bde3Sreinoud 	free(dscr);
867e2036ad8Sreinoud 
868e2036ad8Sreinoud 	/* remember our location for hardlinks */
869e2036ad8Sreinoud 	cur->inode->fsuse = malloc(sizeof(struct long_ad));
870e2036ad8Sreinoud 	memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad));
871e2036ad8Sreinoud 
8723551bde3Sreinoud 	return error;
873e2036ad8Sreinoud }
874e2036ad8Sreinoud 
875e2036ad8Sreinoud 
876e2036ad8Sreinoud static int
udf_populate_walk(fsinfo_t * fsopts,fsnode * root,char * dir,struct long_ad * parent_icb,struct long_ad * dir_icb)877e2036ad8Sreinoud udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir,
878e2036ad8Sreinoud 		struct long_ad *parent_icb, struct long_ad *dir_icb)
879e2036ad8Sreinoud {
880e2036ad8Sreinoud 	union dscrptr *dir_dscr, *dscr;
881e2036ad8Sreinoud 	struct fileid_desc *fid;
882e2036ad8Sreinoud 	struct long_ad icb, data_icb, dirdata_icb;
883e2036ad8Sreinoud 	fsnode *cur;
884e2036ad8Sreinoud 	fsinode *fnode;
885e2036ad8Sreinoud 	size_t pathlen = strlen(dir);
886e2036ad8Sreinoud 	size_t dirlen;
887e2036ad8Sreinoud 	char *mydir = dir + pathlen;
888e2036ad8Sreinoud 	uint32_t nblk, ddoff;
889e2036ad8Sreinoud 	uint32_t softlink_len;
890e2036ad8Sreinoud 	uint8_t *softlink_buf;
891e2036ad8Sreinoud 	uint8_t *dirdata;
892e2036ad8Sreinoud 	int error, ret, retval;
893e2036ad8Sreinoud 
894e2036ad8Sreinoud 	/* claim disc space for the (e)fe descriptor for this dir */
895e2036ad8Sreinoud 	udf_metadata_alloc(1, dir_icb);
896e2036ad8Sreinoud 
897e2036ad8Sreinoud 	/* create new e(fe) */
898e2036ad8Sreinoud 	udf_create_new_file(&root->inode->st, &dir_dscr,
899e2036ad8Sreinoud 		UDF_ICB_FILETYPE_DIRECTORY, dir_icb);
900e2036ad8Sreinoud 
901e2036ad8Sreinoud 	/* allocate memory for the directory contents */
9028f4e1cd9Sreinoud 	dirlen = root->inode->st.st_size;
9038f4e1cd9Sreinoud 	nblk = UDF_ROUNDUP(dirlen, context.sector_size) / context.sector_size;
904e2036ad8Sreinoud 	dirdata = malloc(nblk * context.sector_size);
905e2036ad8Sreinoud 	assert(dirdata);
906e2036ad8Sreinoud 	memset(dirdata, 0, nblk * context.sector_size);
907e2036ad8Sreinoud 
908e2036ad8Sreinoud 	/* create and append '..' */
909e2036ad8Sreinoud 	fid = (struct fileid_desc *) dirdata;
910e2036ad8Sreinoud 	ddoff = udf_create_parentfid(fid, parent_icb);
9118f4e1cd9Sreinoud 	assert(ddoff == 40);
912e2036ad8Sreinoud 
913e2036ad8Sreinoud 	/* for '..' */
914e2036ad8Sreinoud 	udf_inc_link(dir_dscr);
915e2036ad8Sreinoud 
916e2036ad8Sreinoud 	/* recurse */
917e2036ad8Sreinoud 	retval = 0;
918e2036ad8Sreinoud 	for (cur = root; cur != NULL; cur = cur->next) {
919e2036ad8Sreinoud 		mydir[0] = '/';
920e2036ad8Sreinoud 		strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen);
921e2036ad8Sreinoud 
922e2036ad8Sreinoud 		fid = (struct fileid_desc *) (dirdata + ddoff);
923e2036ad8Sreinoud 		switch (cur->type & S_IFMT) {
924e2036ad8Sreinoud 		default:
925e2036ad8Sreinoud 			/* what kind of nodes? */
926e2036ad8Sreinoud 			retval = 2;
927e2036ad8Sreinoud 			break;
928e2036ad8Sreinoud 		case S_IFCHR:
929e2036ad8Sreinoud 		case S_IFBLK:
930e2036ad8Sreinoud 			/* not supported */
931e2036ad8Sreinoud 			retval = 2;
932e2036ad8Sreinoud 			warnx("device node %s not supported", dir);
933e2036ad8Sreinoud 			break;
934e2036ad8Sreinoud 		case S_IFDIR:
935e2036ad8Sreinoud 			/* not an empty dir? */
936e2036ad8Sreinoud 			if (strcmp(cur->name, ".") == 0)
937e2036ad8Sreinoud 				break;
938e2036ad8Sreinoud 			assert(cur->child);
939e2036ad8Sreinoud 			if (cur->child) {
940e2036ad8Sreinoud 				ret = udf_populate_walk(fsopts, cur->child,
941e2036ad8Sreinoud 					dir, dir_icb, &icb);
942e2036ad8Sreinoud 				if (ret)
943e2036ad8Sreinoud 					retval = 2;
944e2036ad8Sreinoud 			}
945e2036ad8Sreinoud 			udf_create_fid(ddoff, fid, cur->name,
946e2036ad8Sreinoud 					UDF_FILE_CHAR_DIR, &icb);
947e2036ad8Sreinoud 			udf_inc_link(dir_dscr);
948e2036ad8Sreinoud 			ddoff += udf_fidsize(fid);
949e2036ad8Sreinoud 			break;
950e2036ad8Sreinoud 		case S_IFREG:
951e2036ad8Sreinoud 			fnode = cur->inode;
952e2036ad8Sreinoud 			/* don't re-copy hard-links */
953e2036ad8Sreinoud 			if (!(fnode->flags & FI_WRITTEN)) {
9548f4e1cd9Sreinoud 				printf("%s\n", dir);
955e2036ad8Sreinoud 				error = udf_copy_file(&fnode->st, dir, cur,
956e2036ad8Sreinoud 					fid, &icb);
957e2036ad8Sreinoud 				if (!error) {
958e2036ad8Sreinoud 					fnode->flags |= FI_WRITTEN;
959e2036ad8Sreinoud 					udf_create_fid(ddoff, fid, cur->name,
960e2036ad8Sreinoud 						0, &icb);
961e2036ad8Sreinoud 					ddoff += udf_fidsize(fid);
962e2036ad8Sreinoud 				} else {
963e2036ad8Sreinoud 					retval = 2;
964e2036ad8Sreinoud 				}
965e2036ad8Sreinoud 			} else {
966e2036ad8Sreinoud 				/* hardlink! */
967e2036ad8Sreinoud 				printf("%s (hardlink)\n", dir);
968e2036ad8Sreinoud 				udf_create_fid(ddoff, fid, cur->name,
969e2036ad8Sreinoud 					0, (struct long_ad *) (fnode->fsuse));
970e2036ad8Sreinoud 				ddoff += udf_fidsize(fid);
971e2036ad8Sreinoud 			}
972e2036ad8Sreinoud 			fnode->nlink--;
973e2036ad8Sreinoud 			if (fnode->nlink == 0)
974e2036ad8Sreinoud 				free(fnode->fsuse);
975e2036ad8Sreinoud 			break;
976e2036ad8Sreinoud 		case S_IFLNK:
977e2036ad8Sreinoud 			/* softlink */
978e2036ad8Sreinoud 			fnode = cur->inode;
979e2036ad8Sreinoud 			printf("%s -> %s\n", dir, cur->symlink);
980e2036ad8Sreinoud 			error = udf_encode_symlink(&softlink_buf,
981e2036ad8Sreinoud 					&softlink_len, cur->symlink);
982e2036ad8Sreinoud 			if (error) {
983e2036ad8Sreinoud 				printf("SOFTLINK error %d\n", error);
984e2036ad8Sreinoud 				retval = 2;
985e2036ad8Sreinoud 				break;
986e2036ad8Sreinoud 			}
987e2036ad8Sreinoud 
988e2036ad8Sreinoud 			udf_metadata_alloc(1, &icb);
989e2036ad8Sreinoud 			udf_create_new_file(&fnode->st, &dscr,
990e2036ad8Sreinoud 				UDF_ICB_FILETYPE_SYMLINK, &icb);
991e2036ad8Sreinoud 
992e2036ad8Sreinoud 			nblk = udf_datablocks(softlink_len);
993e2036ad8Sreinoud 			if (nblk > 0)
994e2036ad8Sreinoud 				udf_data_alloc(nblk, &data_icb);
995e2036ad8Sreinoud 			udf_append_file_contents(dscr, &data_icb,
996e2036ad8Sreinoud 					softlink_buf, softlink_len);
997e2036ad8Sreinoud 
998e2036ad8Sreinoud 			/* write out dscr (e)fe */
999e2036ad8Sreinoud 			udf_inc_link(dscr);
1000e2036ad8Sreinoud 			udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num),
1001e2036ad8Sreinoud 				udf_rw16(icb.loc.part_num), 1);
1002e2036ad8Sreinoud 
10033551bde3Sreinoud 			free(dscr);
1004e2036ad8Sreinoud 			free(softlink_buf);
1005e2036ad8Sreinoud 
1006e2036ad8Sreinoud 			udf_create_fid(ddoff, fid, cur->name, 0, &icb);
1007e2036ad8Sreinoud 			ddoff += udf_fidsize(fid);
1008e2036ad8Sreinoud 			break;
1009e2036ad8Sreinoud 		}
1010e2036ad8Sreinoud 		mydir[0] = '\0';
1011e2036ad8Sreinoud 	}
10128f4e1cd9Sreinoud 	assert(dirlen == ddoff);
1013e2036ad8Sreinoud 
10148f4e1cd9Sreinoud 	/* pre allocate space for the directory contents */
10158f4e1cd9Sreinoud 	memset(&dirdata_icb, 0, sizeof(dirdata_icb));
10168f4e1cd9Sreinoud 	nblk = udf_datablocks(dirlen);
10178f4e1cd9Sreinoud 
10188f4e1cd9Sreinoud 	/* claim disc space for the dir contents if needed */
10198f4e1cd9Sreinoud 	if (nblk > 0)
10208f4e1cd9Sreinoud 		udf_fids_alloc(nblk, &dirdata_icb);
1021e2036ad8Sreinoud 
1022e2036ad8Sreinoud 	udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen);
1023e2036ad8Sreinoud 	udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen);
1024e2036ad8Sreinoud 
1025e2036ad8Sreinoud 	/* write out dir_dscr (e)fe */
1026e2036ad8Sreinoud 	udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num),
1027e2036ad8Sreinoud 			udf_rw16(dir_icb->loc.part_num), 1);
1028e2036ad8Sreinoud 
10293551bde3Sreinoud 	free(dirdata);
10303551bde3Sreinoud 	free(dir_dscr);
1031e2036ad8Sreinoud 	return retval;
1032e2036ad8Sreinoud }
1033e2036ad8Sreinoud 
1034e2036ad8Sreinoud 
1035e2036ad8Sreinoud static int
udf_populate(const char * dir,fsnode * root,fsinfo_t * fsopts,struct udf_stats * stats)1036e2036ad8Sreinoud udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts,
1037e2036ad8Sreinoud 		struct udf_stats *stats)
1038e2036ad8Sreinoud {
1039e2036ad8Sreinoud 	struct long_ad rooticb;
1040e2036ad8Sreinoud 	static char path[MAXPATHLEN+1];
1041e2036ad8Sreinoud 	int error;
1042e2036ad8Sreinoud 
1043e2036ad8Sreinoud 	strncpy(path, dir, sizeof(path));
1044e2036ad8Sreinoud 	error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb);
1045e2036ad8Sreinoud 
1046e2036ad8Sreinoud 	return error;
1047e2036ad8Sreinoud }
1048e2036ad8Sreinoud 
1049e2036ad8Sreinoud 
1050e2036ad8Sreinoud static void
udf_enumerate_and_estimate(const char * dir,fsnode * root,fsinfo_t * fsopts,struct udf_stats * stats)1051e2036ad8Sreinoud udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts,
1052e2036ad8Sreinoud 		struct udf_stats *stats)
1053e2036ad8Sreinoud {
1054e2036ad8Sreinoud 	char path[MAXPATHLEN + 1];
1055f7be5947Sreinoud 	off_t proposed_size;
10568f4e1cd9Sreinoud 	uint32_t n, nblk, nmetablk, nbytes;
10578f4e1cd9Sreinoud 	uint32_t spareable_blocks, spareable_blockingnr;
1058e2036ad8Sreinoud 
1059e2036ad8Sreinoud 	strncpy(path, dir, sizeof(path));
1060e2036ad8Sreinoud 
1061e2036ad8Sreinoud 	/* calculate strict minimal size */
1062e2036ad8Sreinoud 	udf_estimate_walk(fsopts, root, path, stats);
10638f4e1cd9Sreinoud #if 0
10643551bde3Sreinoud 	printf("ndirs            %d\n", stats->ndirs);
10653551bde3Sreinoud 	printf("nfiles           %d\n", stats->nfiles);
10663551bde3Sreinoud 	printf("ndata_blocks     %d\n", stats->ndatablocks);
10673551bde3Sreinoud 	printf("nmetadata_blocks %d\n", stats->nmetadatablocks);
10683551bde3Sreinoud 	printf("\n");
10698f4e1cd9Sreinoud #endif
1070e2036ad8Sreinoud 
1071e2036ad8Sreinoud 	/* adjust for options : free file nodes */
1072e2036ad8Sreinoud 	if (fsopts->freefiles) {
1073e2036ad8Sreinoud 		/* be mercifull and reserve more for the FID */
1074e2036ad8Sreinoud 		stats->nmetadatablocks += fsopts->freefiles * 1.5;
1075e2036ad8Sreinoud 	} else if ((n = fsopts->freefilepc)) {
1076e2036ad8Sreinoud 		stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n);
1077e2036ad8Sreinoud 	}
1078e2036ad8Sreinoud 
1079e2036ad8Sreinoud 	/* adjust for options : free data blocks */
1080e2036ad8Sreinoud 	if (fsopts->freeblocks) {
1081e2036ad8Sreinoud 		stats->ndatablocks += fsopts->freeblocks;
1082e2036ad8Sreinoud 	} else if ((n = fsopts->freeblockpc)) {
1083e2036ad8Sreinoud 		stats->ndatablocks += (stats->ndatablocks * n) / (100-n);
1084e2036ad8Sreinoud 	}
1085e2036ad8Sreinoud 
1086e2036ad8Sreinoud 	/* rough predictor of minimum disc size */
1087e2036ad8Sreinoud 	nblk  = stats->ndatablocks + stats->nmetadatablocks;
10888f4e1cd9Sreinoud 	if (context.format_flags & FORMAT_META) {
10898f4e1cd9Sreinoud 		float meta_p;
10908f4e1cd9Sreinoud 		double factor;
10918f4e1cd9Sreinoud 
10928f4e1cd9Sreinoud 		meta_p = (float) context.meta_perc/100.0;
10938f4e1cd9Sreinoud 		factor = meta_p / (1.0 - meta_p);
10948f4e1cd9Sreinoud 
10958f4e1cd9Sreinoud 		/* add space for metadata partition including some slack */
10968f4e1cd9Sreinoud 		nmetablk = factor * nblk + 32;
10978f4e1cd9Sreinoud 		nblk =  stats->ndatablocks + nmetablk;
10988f4e1cd9Sreinoud 
10998f4e1cd9Sreinoud 		/* free space maps */
11008f4e1cd9Sreinoud 		nbytes = ceil((double) nblk * (1.0/8.0));
11018f4e1cd9Sreinoud 		nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size;
11028f4e1cd9Sreinoud 		if (!(context.format_flags & FORMAT_READONLY)) {
11038f4e1cd9Sreinoud 			nbytes = ceil((double) nmetablk * (1.0/8.0));
11048f4e1cd9Sreinoud 			nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size;
11058f4e1cd9Sreinoud 		}
11068f4e1cd9Sreinoud 	} else if (context.format_flags & FORMAT_SEQUENTIAL) {
11078f4e1cd9Sreinoud 		/* nothing */
11088f4e1cd9Sreinoud 	} else {
11098f4e1cd9Sreinoud 		if (!(context.format_flags & FORMAT_READONLY)) {
11108f4e1cd9Sreinoud 			nbytes = ceil((double) nblk * (1.0/8.0));
11118f4e1cd9Sreinoud 			nblk += 1 + (nbytes + context.sector_size-1)/
11128f4e1cd9Sreinoud 				context.sector_size;
11138f4e1cd9Sreinoud 		}
11148f4e1cd9Sreinoud 	}
11158f4e1cd9Sreinoud 
11168f4e1cd9Sreinoud 	/*
11178f4e1cd9Sreinoud 	 * Make extra room for spareable table if requested
11188f4e1cd9Sreinoud 	 */
11198f4e1cd9Sreinoud 	if (context.format_flags & FORMAT_SPAREABLE) {
11208f4e1cd9Sreinoud 		spareable_blockingnr = udf_spareable_blockingnr();
11218f4e1cd9Sreinoud 		spareable_blocks     = udf_spareable_blocks();
11228f4e1cd9Sreinoud 
11238f4e1cd9Sreinoud 		nblk += spareable_blocks * spareable_blockingnr;
11248f4e1cd9Sreinoud 		nblk += spareable_blockingnr;		/* slack */
11258f4e1cd9Sreinoud 	}
11268f4e1cd9Sreinoud 
1127e2036ad8Sreinoud 	nblk += 256;					/* pre-volume space  */
1128e2036ad8Sreinoud 	nblk += 256;					/* post-volume space */
11298f4e1cd9Sreinoud 	nblk += 1024;					/* safeguard	     */
1130e2036ad8Sreinoud 
1131e2036ad8Sreinoud 	/* try to honour minimum size */
1132e2036ad8Sreinoud 	n = fsopts->minsize / fsopts->sectorsize;
1133e2036ad8Sreinoud 	if (nblk < n) {
1134e2036ad8Sreinoud 		stats->ndatablocks += (n - nblk);
1135e2036ad8Sreinoud 		nblk += n - nblk;
1136e2036ad8Sreinoud 	}
1137945e1caaSreinoud 
1138945e1caaSreinoud 	/* keep proposed size a multiple of blockingnr for image creation */
1139945e1caaSreinoud 	if (S_ISREG(dev_fd_stat.st_mode)) {
1140945e1caaSreinoud 		struct mmc_trackinfo ti;
1141945e1caaSreinoud 		int blockingnr;
1142945e1caaSreinoud 		int error;
1143945e1caaSreinoud 
1144945e1caaSreinoud 		/* adjust proposed size to be a multiple of the blockingnr */
1145945e1caaSreinoud 		udf_update_discinfo();
1146945e1caaSreinoud 		ti.tracknr = mmc_discinfo.first_track_last_session;
1147945e1caaSreinoud 		error = udf_update_trackinfo(&ti);
1148945e1caaSreinoud 		assert(!error);
1149945e1caaSreinoud 		blockingnr = udf_get_blockingnr(&ti);
1150945e1caaSreinoud 		nblk = UDF_ROUNDUP(nblk, blockingnr);
1151945e1caaSreinoud 	}
1152945e1caaSreinoud 
1153f7be5947Sreinoud 	proposed_size = (off_t) nblk * fsopts->sectorsize;
1154945e1caaSreinoud 
1155e2036ad8Sreinoud 	/* sanity size */
1156f7be5947Sreinoud 	if (proposed_size < 512*1024)
1157f7be5947Sreinoud 		proposed_size = 512*1024;
1158f7be5947Sreinoud 
1159f7be5947Sreinoud 	if (fsopts->size) {
1160f7be5947Sreinoud 		if (fsopts->size < proposed_size)
1161*ec5d1d82Stsutsui 			errx(EXIT_FAILURE, "makefs_udf: won't fit on disc!");
1162f7be5947Sreinoud 	} else {
1163f7be5947Sreinoud 		fsopts->size = proposed_size;
1164f7be5947Sreinoud 	}
1165e2036ad8Sreinoud 
1166e2036ad8Sreinoud 	fsopts->inodes = stats->nfiles + stats->ndirs;
1167e2036ad8Sreinoud }
1168e2036ad8Sreinoud 
1169e2036ad8Sreinoud 
1170e2036ad8Sreinoud void
udf_makefs(const char * image,const char * dir,fsnode * root,fsinfo_t * fsopts)1171e2036ad8Sreinoud udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
1172e2036ad8Sreinoud {
1173e2036ad8Sreinoud 	struct udf_stats stats;
1174e2036ad8Sreinoud 	uint64_t truncate_len;
11758f4e1cd9Sreinoud 	uint32_t last_sector, ext;
1176c55e926fSreinoud 	char scrap[255];
1177e2036ad8Sreinoud 	int error;
1178e2036ad8Sreinoud 
11798f4e1cd9Sreinoud 	/* setup */
11808f4e1cd9Sreinoud 	emul_sectorsize = fsopts->sectorsize;
11818f4e1cd9Sreinoud 	emul_size = 0;
1182e2036ad8Sreinoud 	context.sector_size = fsopts->sectorsize;
1183e2036ad8Sreinoud 
1184e2036ad8Sreinoud 	/* names */
1185e2036ad8Sreinoud 	error = udf_proces_names();
1186e2036ad8Sreinoud 	if (error)
1187*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "bad names given");
1188e2036ad8Sreinoud 
11898f4e1cd9Sreinoud 	/* open disc device or emulated file */
11908f4e1cd9Sreinoud 	if (udf_opendisc(image, O_CREAT)) {
11918f4e1cd9Sreinoud 		udf_closedisc();
1192*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "can't open %s", image);
11938f4e1cd9Sreinoud 	}
11948f4e1cd9Sreinoud 	fsopts->fd = dev_fd;
11958f4e1cd9Sreinoud 
11968f4e1cd9Sreinoud 	/* determine format */
11978f4e1cd9Sreinoud 	if (udf_readonly_format())
11988f4e1cd9Sreinoud 		req_enable |= FORMAT_READONLY;
11998f4e1cd9Sreinoud 	// printf("req_enable %d, req_disable %d\n", req_enable, req_disable);
12008f4e1cd9Sreinoud 	error = udf_derive_format(req_enable, req_disable);
12018f4e1cd9Sreinoud 	if (error) {
12028f4e1cd9Sreinoud 		udf_closedisc();
1203*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "can't derive format from media/settings");
12048f4e1cd9Sreinoud 	}
1205e2036ad8Sreinoud 
1206e2036ad8Sreinoud 	/* estimate the amount of space needed */
120762e9bc7cSreinoud 	memset(&stats, 0, sizeof(stats));
1208e2036ad8Sreinoud 	udf_enumerate_and_estimate(dir, root, fsopts, &stats);
1209e2036ad8Sreinoud 
12108f4e1cd9Sreinoud 	printf("Calculated size of `%s' is "
12118f4e1cd9Sreinoud 		"%"PRIu64" KiB, %"PRIu64" MiB, %"PRIu64" GiB with %ld inodes\n",
12128f4e1cd9Sreinoud 		image,
12138f4e1cd9Sreinoud 		(uint64_t) fsopts->size/1024,
12148f4e1cd9Sreinoud 		(uint64_t) fsopts->size/1024/1024,
12158f4e1cd9Sreinoud 		(uint64_t) fsopts->size/1024/1024/1024,
12168f4e1cd9Sreinoud 		(long)fsopts->inodes);
12178f4e1cd9Sreinoud 	emul_size = MAX(emul_size, fsopts->size);
12188f4e1cd9Sreinoud 	if ((fsopts->maxsize > 0) && (emul_size > fsopts->maxsize))
1219*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "won't fit due to set maximum disk size");
1220e2036ad8Sreinoud 
12218f4e1cd9Sreinoud 	/* prepare disc if necessary (recordables mainly) */
12228f4e1cd9Sreinoud 	error = udf_prepare_disc();
12238f4e1cd9Sreinoud 	if (error) {
12248f4e1cd9Sreinoud 		udf_closedisc();
1225*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "preparing disc failed");
1226e2036ad8Sreinoud 	}
1227e2036ad8Sreinoud 
1228e2036ad8Sreinoud 	/* update mmc info but now with correct size */
12298f4e1cd9Sreinoud 	udf_update_discinfo();
12308f4e1cd9Sreinoud 	udf_dump_discinfo(&mmc_discinfo);
1231e2036ad8Sreinoud 
1232c55e926fSreinoud 	printf("Building disc compatible with UDF version %x to %x\n\n",
1233c55e926fSreinoud 		context.min_udf, context.max_udf);
1234c55e926fSreinoud 	(void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS,
12358f4e1cd9Sreinoud 	    (uint64_t) context.format_flags);
1236c55e926fSreinoud 	printf("UDF properties       %s\n", scrap);
1237c55e926fSreinoud 	printf("Volume set          `%s'\n", context.volset_name);
1238c55e926fSreinoud 	printf("Primary volume      `%s`\n", context.primary_name);
1239c55e926fSreinoud 	printf("Logical volume      `%s`\n", context.logvol_name);
12408f4e1cd9Sreinoud 	if (context.format_flags & FORMAT_META)
12418f4e1cd9Sreinoud 		printf("Metadata percentage  %d%% (%d%% used)\n",
12428f4e1cd9Sreinoud 			context.meta_perc,
12437906b5d4Sreinoud 			(int) ceil(100.0*stats.nmetadatablocks/stats.ndatablocks));
1244c55e926fSreinoud 	printf("\n");
12458f4e1cd9Sreinoud 
12468f4e1cd9Sreinoud 	/* prefix */
12478f4e1cd9Sreinoud 	udf_allow_writing();
12488f4e1cd9Sreinoud 	if (udf_do_newfs_prefix()) {
12498f4e1cd9Sreinoud 		udf_closedisc();
1250*ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "basic setup failed");
12518f4e1cd9Sreinoud 	}
1252e2036ad8Sreinoud 
1253e2036ad8Sreinoud 	/* update context */
1254e2036ad8Sreinoud 	context.unique_id = 0;
1255e2036ad8Sreinoud 
12568f4e1cd9Sreinoud 	/* add all directories */
12578f4e1cd9Sreinoud 	error = udf_populate(dir, root, fsopts, &stats);
12588f4e1cd9Sreinoud 
12598f4e1cd9Sreinoud 	if (!error) {
1260374cb9beSwiz 		/* update values for integrity sequence */
1261e2036ad8Sreinoud 		context.num_files = stats.nfiles;
1262e2036ad8Sreinoud 		context.num_directories = stats.ndirs;
1263e2036ad8Sreinoud 
1264e2036ad8Sreinoud 		udf_do_newfs_postfix();
1265e2036ad8Sreinoud 
12668f4e1cd9Sreinoud 		if (S_ISREG(dev_fd_stat.st_mode) &&
12678f4e1cd9Sreinoud 				(context.format_flags & FORMAT_VAT)) {
12688f4e1cd9Sreinoud 			udf_translate_vtop(context.alloc_pos[context.data_part],
12698f4e1cd9Sreinoud 				context.data_part,
12708f4e1cd9Sreinoud 				&last_sector, &ext);
12718f4e1cd9Sreinoud 			truncate_len = (uint64_t) last_sector * context.sector_size;
1272e2036ad8Sreinoud 
1273e2036ad8Sreinoud 			printf("\nTruncing the disc-image to allow for VAT\n");
1274f7be5947Sreinoud 			printf("Free space left on this volume approx. "
1275f7be5947Sreinoud 				"%"PRIu64" KiB, %"PRIu64" MiB\n",
1276f7be5947Sreinoud 				(fsopts->size - truncate_len)/1024,
1277f7be5947Sreinoud 				(fsopts->size - truncate_len)/1024/1024);
12788f4e1cd9Sreinoud 			ftruncate(dev_fd, truncate_len);
1279e2036ad8Sreinoud 		}
1280e2036ad8Sreinoud 	}
12818f4e1cd9Sreinoud 	udf_closedisc();
1282e2036ad8Sreinoud 
12838f4e1cd9Sreinoud 	if (error == 2)
12848f4e1cd9Sreinoud 		errx(error, "not all files could be added");
12858f4e1cd9Sreinoud 	if (error == 1)
1286e2036ad8Sreinoud 		errx(error, "creation of %s failed", image);
12878f4e1cd9Sreinoud 	return;
1288e2036ad8Sreinoud }
1289