xref: /netbsd-src/sbin/newfs_udf/newfs_udf.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* $NetBSD: newfs_udf.c,v 1.24 2022/04/09 09:58:11 riastradh Exp $ */
2 
3 /*
4  * Copyright (c) 2006, 2008, 2013, 2021, 2022 Reinoud Zandijk
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 /*
30  * TODO
31  * - implement metadata formatting for BD-R
32  * - implement support for a read-only companion partition?
33  */
34 
35 #define _EXPOSE_MMC
36 #if 0
37 # define DEBUG
38 #endif
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <dirent.h>
43 #include <inttypes.h>
44 #include <stdint.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <util.h>
50 #include <time.h>
51 #include <assert.h>
52 #include <err.h>
53 
54 #include <sys/ioctl.h>
55 #include <sys/stat.h>
56 #include <sys/types.h>
57 #include <sys/cdio.h>
58 #include <sys/disklabel.h>
59 #include <sys/dkio.h>
60 #include <sys/param.h>
61 
62 #include <fs/udf/udf_mount.h>
63 
64 #include "mountprog.h"
65 #include "udf_core.h"
66 #include "newfs_udf.h"
67 
68 /* Identifying myself */
69 #define IMPL_NAME		"*NetBSD newfs_udf 10.0"
70 #define APP_VERSION_MAIN	0
71 #define APP_VERSION_SUB		5
72 
73 /* prototypes */
74 int newfs_udf(int argc, char **argv);
75 static void usage(void) __attribute__((__noreturn__));
76 
77 /* global variables describing disc and format requests */
78 char	*format_str;			/* format: string representation */
79 
80 
81 /* --------------------------------------------------------------------- */
82 
83 static int
84 udf_prepare_format_track512(void)
85 {
86 	struct mmc_trackinfo ti;
87 	struct mmc_op        op;
88 	int error;
89 
90 	if (!(context.format_flags & FORMAT_TRACK512))
91 		return 0;
92 
93 	/* get last track (again) */
94 	ti.tracknr = mmc_discinfo.last_track_last_session;
95 	error = udf_update_trackinfo(&ti);
96 	if (error)
97 		return error;
98 
99 	/* Split up the space at 512 for iso cd9660 hooking */
100 	memset(&op, 0, sizeof(op));
101 	op.operation   = MMC_OP_RESERVETRACK_NWA;	/* UPTO nwa */
102 	op.mmc_profile = mmc_discinfo.mmc_profile;
103 	op.extent      = 512;				/* size */
104 	error = ioctl(dev_fd, MMCOP, &op);
105 
106 	return error;
107 }
108 
109 /* --------------------------------------------------------------------- */
110 
111 static int
112 udf_do_newfs(void)
113 {
114 	int error;
115 
116 	error = udf_do_newfs_prefix();
117 	if (error)
118 		return error;
119 	error = udf_do_rootdir();
120 	if (error)
121 		return error;
122 	error = udf_do_newfs_postfix();
123 
124 	return error;
125 }
126 
127 /* --------------------------------------------------------------------- */
128 
129 static void
130 usage(void)
131 {
132 	(void)fprintf(stderr, "Usage: %s [-cFM] [-L loglabel] "
133 	    "[-P discid] [-S sectorsize] [-s size] [-p perc] "
134 	    "[-t gmtoff] [-v min_udf] [-V max_udf] special\n", getprogname());
135 	exit(EXIT_FAILURE);
136 }
137 
138 
139 int
140 main(int argc, char **argv)
141 {
142 	struct tm *tm;
143 	time_t now;
144 	off_t setsize;
145 	char  scrap[255], *colon;
146 	int ch, req_enable, req_disable;
147 	int error;
148 
149 	setprogname(argv[0]);
150 
151 	/* initialise */
152 	format_str    = strdup("");
153 	req_enable    = req_disable = 0;
154 	setsize       = 0;
155 
156 	emul_mmc_profile  =  -1;	/* invalid->no emulation	*/
157 	emul_packetsize   =   1;	/* reasonable default		*/
158 	emul_sectorsize   = 512;	/* minimum allowed sector size	*/
159 	emul_size	  =   0;	/* empty			*/
160 
161 	srandom((unsigned long) time(NULL));
162 	udf_init_create_context();
163 	context.app_name         = "*NetBSD UDF";
164 	context.app_version_main = APP_VERSION_MAIN;
165 	context.app_version_sub  = APP_VERSION_SUB;
166 	context.impl_name        = IMPL_NAME;
167 
168 	/* minimum and maximum UDF versions we advise */
169 	context.min_udf = 0x201;
170 	context.max_udf = 0x250;
171 
172 	/* use user's time zone as default */
173 	(void)time(&now);
174 	tm = localtime(&now);
175 	context.gmtoff = tm->tm_gmtoff;
176 
177 	/* process options */
178 	while ((ch = getopt(argc, argv, "cFL:Mp:P:s:S:B:t:v:V:")) != -1) {
179 		switch (ch) {
180 		case 'c' :
181 			context.check_surface = 1;
182 			break;
183 		case 'F' :
184 			context.create_new_session = 1;
185 			break;
186 		case 'L' :
187 			if (context.logvol_name) free(context.logvol_name);
188 			context.logvol_name = strdup(optarg);
189 			break;
190 		case 'M' :
191 			req_disable |= FORMAT_META;
192 			break;
193 		case 'p' :
194 			context.meta_perc = a_num(optarg, "meta_perc");
195 			/* limit to `sensible` values */
196 			context.meta_perc = MIN(context.meta_perc, 99);
197 			context.meta_perc = MAX(context.meta_perc, 1);
198 			break;
199 		case 'v' :
200 			context.min_udf = a_udf_version(optarg, "min_udf");
201 			if (context.min_udf > context.max_udf)
202 				context.max_udf = context.min_udf;
203 			break;
204 		case 'V' :
205 			context.max_udf = a_udf_version(optarg, "max_udf");
206 			if (context.min_udf > context.max_udf)
207 				context.min_udf = context.max_udf;
208 			break;
209 		case 'P' :
210 			/* check if there is a ':' in the name */
211 			if ((colon = strstr(optarg, ":"))) {
212 				if (context.volset_name)
213 					free(context.volset_name);
214 				*colon = 0;
215 				context.volset_name = strdup(optarg);
216 				optarg = colon+1;
217 			}
218 			if (context.primary_name)
219 				free(context.primary_name);
220 			if ((strstr(optarg, ":")))
221 				errx(1, "primary name can't have ':' in its name");
222 			context.primary_name = strdup(optarg);
223 			break;
224 		case 's' :
225 			/* support for files, set file size */
226 			/* XXX support for formatting recordables on vnd/file? */
227 			if (dehumanize_number(optarg, &setsize) < 0)
228 				errx(1, "can't parse size argument");
229 			setsize = MAX(0, setsize);
230 			break;
231 		case 'S' :
232 			emul_sectorsize = a_num(optarg, "secsize");
233 			emul_sectorsize = MAX(512, emul_sectorsize);
234 			break;
235 		case 'B' :
236 			emul_packetsize = a_num(optarg,
237 				"blockingnr, packetsize");
238 			emul_packetsize = MAX(emul_packetsize, 1);
239 			emul_packetsize = MIN(emul_packetsize, 32);
240 			break;
241 		case 't' :
242 			/* time zone override */
243 			context.gmtoff = a_num(optarg, "gmtoff");
244 			break;
245 		default  :
246 			usage();
247 			/* NOTREACHED */
248 		}
249 	}
250 
251 	if (optind + 1 != argc)
252 		usage();
253 
254 	/* get device and directory specifier */
255 	dev_name = argv[optind];
256 
257 	emul_size = setsize;
258 	error = udf_opendisc(dev_name, O_RDWR | O_CREAT | O_TRUNC);
259 	if (error) {
260 		udf_closedisc();
261 		errx(1, "can't open device");
262 	}
263 
264 	/* get 'disc' information */
265 	error = udf_update_discinfo();
266 	if (error) {
267 		udf_closedisc();
268 		errx(1, "can't retrieve discinfo");
269 	}
270 
271 	/* derive disc identifiers when not specified and check given */
272 	error = udf_proces_names();
273 	if (error) {
274 		/* error message has been printed */
275 		udf_closedisc();
276 		errx(1, "bad names given");
277 	}
278 
279 	/* derive newfs disc format from disc profile */
280 	error = udf_derive_format(req_enable, req_disable);
281 	if (error)  {
282 		/* error message has been printed */
283 		udf_closedisc();
284 		errx(1, "can't derive format from media/settings");
285 	}
286 
287 	udf_dump_discinfo(&mmc_discinfo);
288 	printf("Formatting disc compatible with UDF version %x to %x\n\n",
289 		context.min_udf, context.max_udf);
290 	(void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS,
291 	    (uint64_t) context.format_flags);
292 	printf("UDF properties       %s\n", scrap);
293 	printf("Volume set          `%s'\n", context.volset_name);
294 	printf("Primary volume      `%s`\n", context.primary_name);
295 	printf("Logical volume      `%s`\n", context.logvol_name);
296 	if (context.format_flags & FORMAT_META)
297 		printf("Metadata percentage  %d %%\n", context.meta_perc);
298 	printf("\n");
299 
300 	/* prepare disc if necessary (recordables mainly) */
301 	error = udf_prepare_disc();
302 	if (error) {
303 		udf_closedisc();
304 		errx(1, "preparing disc failed");
305 	}
306 	error = udf_prepare_format_track512();
307 	if (error) {
308 		udf_closedisc();
309 		errx(1, "reservation for track512 failed");
310 	}
311 
312 	/* perform the newfs itself */
313 	udf_allow_writing();
314 	error = udf_do_newfs();
315 	udf_closedisc();
316 
317 	if (error)
318 		return EXIT_FAILURE;
319 
320 	return EXIT_SUCCESS;
321 }
322 
323 /* --------------------------------------------------------------------- */
324