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
udf_prepare_format_track512(void)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
udf_do_newfs(void)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
usage(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
main(int argc,char ** argv)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