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