1 /* $NetBSD: newfs_udf.c,v 1.18 2013/08/09 15:11:08 reinoud Exp $ */
4 * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
31 * - implement metadata formatting for BD-R
32 * - implement support for a read-only companion partition?
54 #include <sys/ioctl.h>
56 #include <sys/types.h>
58 #include <sys/disklabel.h>
60 #include <sys/param.h>
61 #include <sys/queue.h>
63 #include <fs/udf/ecma167-udf.h>
64 #include <fs/udf/udf_mount.h>
66 #include "mountprog.h"
67 #include "udf_create.h"
68 #include "udf_write.h"
69 #include "newfs_udf.h"
72 int newfs_udf(int argc
, char **argv
);
73 static void usage(void) __attribute__((__noreturn__
));
76 /* queue for temporary storage of sectors to be written out */
80 TAILQ_ENTRY(wrsect
) next
;
83 /* write queue and track blocking skew */
84 TAILQ_HEAD(wrsect_list
, wrsect
) write_queue
;
87 /* global variables describing disc and format requests */
88 int fd
; /* device: file descriptor */
89 char *dev
; /* device: name */
90 struct mmc_discinfo mmc_discinfo
; /* device: disc info */
92 char *format_str
; /* format: string representation */
93 int format_flags
; /* format: attribute flags */
94 int media_accesstype
; /* derived from current mmc cap */
95 int check_surface
; /* for rewritables */
96 int imagefile_secsize
; /* for files */
97 int emul_packetsize
; /* for discs and files */
100 int meta_perc
= UDF_META_PERC
;
101 float meta_fract
= (float) UDF_META_PERC
/ 100.0;
104 /* --------------------------------------------------------------------- */
107 * write queue implementation
111 udf_write_sector(void *sector
, uint64_t location
)
113 struct wrsect
*pos
, *seekpos
;
116 /* search location */
117 TAILQ_FOREACH_REVERSE(seekpos
, &write_queue
, wrsect_list
, next
) {
118 if (seekpos
->sectornr
<= location
)
121 if ((seekpos
== NULL
) || (seekpos
->sectornr
!= location
)) {
122 pos
= calloc(1, sizeof(struct wrsect
));
125 /* allocate space for copy of sector data */
126 pos
->sector_data
= calloc(1, context
.sector_size
);
127 if (pos
->sector_data
== NULL
)
129 pos
->sectornr
= location
;
132 TAILQ_INSERT_AFTER(&write_queue
, seekpos
, pos
, next
);
134 TAILQ_INSERT_HEAD(&write_queue
, pos
, next
);
139 memcpy(pos
->sector_data
, sector
, context
.sector_size
);
146 * Now all write requests are queued in the TAILQ, write them out to the
147 * disc/file image. Special care needs to be taken for devices that are only
148 * strict overwritable i.e. only in packet size chunks
150 * XXX support for growing vnd?
154 writeout_write_queue(void)
158 uint64_t line_start
, new_line_start
;
159 uint32_t line_len
, line_offset
, relpos
;
161 uint8_t *linebuf
, *adr
;
163 blockingnr
= layout
.blockingnr
;
164 line_len
= blockingnr
* context
.sector_size
;
165 line_offset
= wrtrack_skew
* context
.sector_size
;
167 linebuf
= malloc(line_len
);
171 pos
= TAILQ_FIRST(&write_queue
);
172 bzero(linebuf
, line_len
);
175 * Always writing out in whole lines now; this is slightly wastefull
176 * on logical overwrite volumes but it reduces complexity and the loss
177 * is near zero compared to disc size.
179 line_start
= (pos
->sectornr
- wrtrack_skew
) / blockingnr
;
180 TAILQ_FOREACH(pos
, &write_queue
, next
) {
181 new_line_start
= (pos
->sectornr
- wrtrack_skew
) / blockingnr
;
182 if (new_line_start
!= line_start
) {
184 offset
= (uint64_t) line_start
* line_len
+ line_offset
;
186 printf("WRITEOUT %08"PRIu64
" + %02d -- "
187 "[%08"PRIu64
"..%08"PRIu64
"]\n",
188 offset
/ context
.sector_size
, blockingnr
,
189 offset
/ context
.sector_size
,
190 offset
/ context
.sector_size
+ blockingnr
-1);
192 if (pwrite(fd
, linebuf
, line_len
, offset
) < 0) {
193 perror("Writing failed");
196 line_start
= new_line_start
;
197 bzero(linebuf
, line_len
);
200 relpos
= (pos
->sectornr
- wrtrack_skew
) % blockingnr
;
201 adr
= linebuf
+ relpos
* context
.sector_size
;
202 memcpy(adr
, pos
->sector_data
, context
.sector_size
);
204 /* writeout last chunk */
205 offset
= (uint64_t) line_start
* line_len
+ line_offset
;
207 printf("WRITEOUT %08"PRIu64
" + %02d -- [%08"PRIu64
"..%08"PRIu64
"]\n",
208 offset
/ context
.sector_size
, blockingnr
,
209 offset
/ context
.sector_size
,
210 offset
/ context
.sector_size
+ blockingnr
-1);
212 if (pwrite(fd
, linebuf
, line_len
, offset
) < 0) {
213 perror("Writing failed");
221 /* --------------------------------------------------------------------- */
224 * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main
225 * code in sys/fs/udf/
230 udf_dump_discinfo(struct mmc_discinfo
*di
)
234 printf("Device/media info :\n");
235 printf("\tMMC profile 0x%02x\n", di
->mmc_profile
);
236 printf("\tderived class %d\n", di
->mmc_class
);
237 printf("\tsector size %d\n", di
->sector_size
);
238 printf("\tdisc state %d\n", di
->disc_state
);
239 printf("\tlast ses state %d\n", di
->last_session_state
);
240 printf("\tbg format state %d\n", di
->bg_format_state
);
241 printf("\tfrst track %d\n", di
->first_track
);
242 printf("\tfst on last ses %d\n", di
->first_track_last_session
);
243 printf("\tlst on last ses %d\n", di
->last_track_last_session
);
244 printf("\tlink block penalty %d\n", di
->link_block_penalty
);
245 snprintb(bits
, sizeof(bits
), MMC_DFLAGS_FLAGBITS
, (uint64_t) di
->disc_flags
);
246 printf("\tdisc flags %s\n", bits
);
247 printf("\tdisc id %x\n", di
->disc_id
);
248 printf("\tdisc barcode %"PRIx64
"\n", di
->disc_barcode
);
250 printf("\tnum sessions %d\n", di
->num_sessions
);
251 printf("\tnum tracks %d\n", di
->num_tracks
);
253 snprintb(bits
, sizeof(bits
), MMC_CAP_FLAGBITS
, di
->mmc_cur
);
254 printf("\tcapabilities cur %s\n", bits
);
255 snprintb(bits
, sizeof(bits
), MMC_CAP_FLAGBITS
, di
->mmc_cap
);
256 printf("\tcapabilities cap %s\n", bits
);
258 printf("\tlast_possible_lba %d\n", di
->last_possible_lba
);
262 #define udf_dump_discinfo(a);
265 /* --------------------------------------------------------------------- */
268 udf_update_discinfo(struct mmc_discinfo
*di
)
271 struct disklabel disklab
;
272 struct partition
*dp
;
273 off_t size
, sectors
, secsize
;
276 memset(di
, 0, sizeof(struct mmc_discinfo
));
278 /* check if we're on a MMC capable device, i.e. CD/DVD */
279 error
= ioctl(fd
, MMCGETDISCINFO
, di
);
283 /* (re)fstat the file */
286 if (S_ISREG(st
.st_mode
)) {
287 /* file support; we pick the minimum sector size allowed */
289 secsize
= imagefile_secsize
;
290 sectors
= size
/ secsize
;
293 * disc partition support; note we can't use DIOCGPART in
294 * userland so get disc label and use the stat info to get the
297 if (ioctl(fd
, DIOCGDINFO
, &disklab
) == -1) {
298 /* failed to get disclabel! */
303 /* get disk partition it refers to */
305 partnr
= DISKPART(st
.st_rdev
);
306 dp
= &disklab
.d_partitions
[partnr
];
308 /* TODO problem with last_possible_lba on resizable VND */
309 if (dp
->p_size
== 0) {
310 perror("faulty disklabel partition returned, "
315 sectors
= dp
->p_size
;
316 secsize
= disklab
.d_secsize
;
319 /* set up a disc info profile for partitions */
320 di
->mmc_profile
= 0x01; /* disc type */
321 di
->mmc_class
= MMC_CLASS_DISC
;
322 di
->disc_state
= MMC_STATE_CLOSED
;
323 di
->last_session_state
= MMC_STATE_CLOSED
;
324 di
->bg_format_state
= MMC_BGFSTATE_COMPLETED
;
325 di
->link_block_penalty
= 0;
327 di
->mmc_cur
= MMC_CAP_RECORDABLE
| MMC_CAP_REWRITABLE
|
328 MMC_CAP_ZEROLINKBLK
| MMC_CAP_HW_DEFECTFREE
;
329 di
->mmc_cap
= di
->mmc_cur
;
330 di
->disc_flags
= MMC_DFLAGS_UNRESTRICTED
;
332 di
->last_possible_lba
= sectors
- 1;
333 di
->sector_size
= secsize
;
335 di
->num_sessions
= 1;
339 di
->first_track_last_session
= di
->last_track_last_session
= 1;
346 udf_update_trackinfo(struct mmc_discinfo
*di
, struct mmc_trackinfo
*ti
)
350 class = di
->mmc_class
;
351 if (class != MMC_CLASS_DISC
) {
352 /* tracknr specified in struct ti */
353 error
= ioctl(fd
, MMCGETTRACKINFO
, ti
);
357 /* discs partition support */
358 if (ti
->tracknr
!= 1)
361 /* create fake ti (TODO check for resized vnds) */
364 ti
->track_mode
= 0; /* XXX */
365 ti
->data_mode
= 0; /* XXX */
366 ti
->flags
= MMC_TRACKINFO_LRA_VALID
| MMC_TRACKINFO_NWA_VALID
;
369 ti
->packet_size
= emul_packetsize
;
371 /* TODO support for resizable vnd */
372 ti
->track_size
= di
->last_possible_lba
;
373 ti
->next_writable
= di
->last_possible_lba
;
374 ti
->last_recorded
= ti
->next_writable
;
382 udf_setup_writeparams(struct mmc_discinfo
*di
)
384 struct mmc_writeparams mmc_writeparams
;
387 if (di
->mmc_class
== MMC_CLASS_DISC
)
391 * only CD burning normally needs setting up, but other disc types
392 * might need other settings to be made. The MMC framework will set up
393 * the nessisary recording parameters according to the disc
394 * characteristics read in. Modifications can be made in the discinfo
395 * structure passed to change the nature of the disc.
397 memset(&mmc_writeparams
, 0, sizeof(struct mmc_writeparams
));
398 mmc_writeparams
.mmc_class
= di
->mmc_class
;
399 mmc_writeparams
.mmc_cur
= di
->mmc_cur
;
402 * UDF dictates first track to determine track mode for the whole
403 * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1]
404 * To prevent problems with a `reserved' track in front we start with
405 * the 2nd track and if that is not valid, go for the 1st.
407 mmc_writeparams
.tracknr
= 2;
408 mmc_writeparams
.data_mode
= MMC_DATAMODE_DEFAULT
; /* XA disc */
409 mmc_writeparams
.track_mode
= MMC_TRACKMODE_DEFAULT
; /* data */
411 error
= ioctl(fd
, MMCSETUPWRITEPARAMS
, &mmc_writeparams
);
413 mmc_writeparams
.tracknr
= 1;
414 error
= ioctl(fd
, MMCSETUPWRITEPARAMS
, &mmc_writeparams
);
421 udf_synchronise_caches(void)
423 struct mmc_op mmc_op
;
425 bzero(&mmc_op
, sizeof(struct mmc_op
));
426 mmc_op
.operation
= MMC_OP_SYNCHRONISECACHE
;
428 /* this device might not know this ioct, so just be ignorant */
429 (void) ioctl(fd
, MMCOP
, &mmc_op
);
432 /* --------------------------------------------------------------------- */
435 udf_prepare_disc(void)
437 struct mmc_trackinfo ti
;
441 /* If the last track is damaged, repair it */
442 ti
.tracknr
= mmc_discinfo
.last_track_last_session
;
443 error
= udf_update_trackinfo(&mmc_discinfo
, &ti
);
447 if (ti
.flags
& MMC_TRACKINFO_DAMAGED
) {
449 * Need to repair last track before anything can be done.
450 * this is an optional command, so ignore its error but report
453 memset(&op
, 0, sizeof(op
));
454 op
.operation
= MMC_OP_REPAIRTRACK
;
455 op
.mmc_profile
= mmc_discinfo
.mmc_profile
;
456 op
.tracknr
= ti
.tracknr
;
457 error
= ioctl(fd
, MMCOP
, &op
);
460 (void)printf("Drive can't explicitly repair last "
461 "damaged track, but it might autorepair\n");
463 /* last track (if any) might not be damaged now, operations are ok now */
465 /* setup write parameters from discinfo */
466 error
= udf_setup_writeparams(&mmc_discinfo
);
470 /* if the drive is not sequential, we're done */
471 if ((mmc_discinfo
.mmc_cur
& MMC_CAP_SEQUENTIAL
) == 0)
475 /* if last track is not the reserved but an empty track, unreserve it */
476 if (ti
.flags
& MMC_TRACKINFO_BLANK
) {
477 if (ti
.flags
& MMC_TRACKINFO_RESERVED
== 0) {
478 memset(&op
, 0, sizeof(op
));
479 op
.operation
= MMC_OP_UNRESERVETRACK
;
480 op
.mmc_profile
= mmc_discinfo
.mmc_profile
;
481 op
.tracknr
= ti
.tracknr
;
482 error
= ioctl(fd
, MMCOP
, &op
);
486 /* update discinfo since it changed by the operation */
487 error
= udf_update_discinfo(&mmc_discinfo
);
494 /* close the last session if its still open */
495 if (mmc_discinfo
.last_session_state
== MMC_STATE_INCOMPLETE
) {
496 printf("Closing last open session if present\n");
497 /* close all associated tracks */
498 tracknr
= mmc_discinfo
.first_track_last_session
;
499 while (tracknr
<= mmc_discinfo
.last_track_last_session
) {
500 ti
.tracknr
= tracknr
;
501 error
= udf_update_trackinfo(&mmc_discinfo
, &ti
);
504 printf("\tClosing open track %d\n", tracknr
);
505 memset(&op
, 0, sizeof(op
));
506 op
.operation
= MMC_OP_CLOSETRACK
;
507 op
.mmc_profile
= mmc_discinfo
.mmc_profile
;
508 op
.tracknr
= tracknr
;
509 error
= ioctl(fd
, MMCOP
, &op
);
514 printf("Closing session\n");
515 memset(&op
, 0, sizeof(op
));
516 op
.operation
= MMC_OP_CLOSESESSION
;
517 op
.mmc_profile
= mmc_discinfo
.mmc_profile
;
518 op
.sessionnr
= mmc_discinfo
.num_sessions
;
519 error
= ioctl(fd
, MMCOP
, &op
);
523 /* update discinfo since it changed by the operations */
524 error
= udf_update_discinfo(&mmc_discinfo
);
529 if (format_flags
& FORMAT_TRACK512
) {
530 /* get last track again */
531 ti
.tracknr
= mmc_discinfo
.last_track_last_session
;
532 error
= udf_update_trackinfo(&mmc_discinfo
, &ti
);
536 /* Split up the space at 512 for iso cd9660 hooking */
537 memset(&op
, 0, sizeof(op
));
538 op
.operation
= MMC_OP_RESERVETRACK_NWA
; /* UPTO nwa */
539 op
.mmc_profile
= mmc_discinfo
.mmc_profile
;
540 op
.extent
= 512; /* size */
541 error
= ioctl(fd
, MMCOP
, &op
);
549 /* --------------------------------------------------------------------- */
552 udf_surface_check(void)
554 uint32_t loc
, block_bytes
;
555 uint32_t sector_size
, blockingnr
, bpos
;
557 int error
, num_errors
;
559 sector_size
= context
.sector_size
;
560 blockingnr
= layout
.blockingnr
;
562 block_bytes
= layout
.blockingnr
* sector_size
;
563 if ((buffer
= malloc(block_bytes
)) == NULL
)
566 /* set all one to not kill Flash memory? */
567 for (bpos
= 0; bpos
< block_bytes
; bpos
++)
570 printf("\nChecking disc surface : phase 1 - writing\n");
572 loc
= layout
.first_lba
;
573 while (loc
<= layout
.last_lba
) {
574 /* write blockingnr sectors */
575 error
= pwrite(fd
, buffer
, block_bytes
, loc
*sector_size
);
576 printf(" %08d + %d (%02d %%)\r", loc
, blockingnr
,
577 (int)((100.0 * loc
)/layout
.last_lba
));
581 printf("BAD block at %08d + %d \n",
582 loc
, layout
.blockingnr
);
583 if ((error
= udf_register_bad_block(loc
))) {
589 loc
+= layout
.blockingnr
;
592 printf("\nChecking disc surface : phase 2 - reading\n");
594 loc
= layout
.first_lba
;
595 while (loc
<= layout
.last_lba
) {
596 /* read blockingnr sectors */
597 error
= pread(fd
, buffer
, block_bytes
, loc
*sector_size
);
598 printf(" %08d + %d (%02d %%)\r", loc
, blockingnr
,
599 (int)((100.0 * loc
)/layout
.last_lba
));
603 printf("BAD block at %08d + %d \n",
604 loc
, layout
.blockingnr
);
605 if ((error
= udf_register_bad_block(loc
))) {
611 loc
+= layout
.blockingnr
;
613 printf("Scan complete : %d bad blocks found\n", num_errors
);
620 /* --------------------------------------------------------------------- */
627 error
= udf_do_newfs_prefix();
630 error
= udf_do_rootdir();
633 error
= udf_do_newfs_postfix();
640 /* --------------------------------------------------------------------- */
645 (void)fprintf(stderr
, "Usage: %s [-cFM] [-L loglabel] "
646 "[-P discid] [-S sectorsize] [-s size] [-p perc] "
647 "[-t gmtoff] [-v min_udf] [-V max_udf] special\n", getprogname());
653 main(int argc
, char **argv
)
659 char scrap
[255], *colon
;
660 int ch
, req_enable
, req_disable
, force
;
663 setprogname(argv
[0]);
666 format_str
= strdup("");
667 req_enable
= req_disable
= 0;
668 format_flags
= FORMAT_INVALID
;
672 imagefile_secsize
= 512; /* minimum allowed sector size */
673 emul_packetsize
= 32; /* reasonable default */
675 srandom((unsigned long) time(NULL
));
676 udf_init_create_context();
677 context
.app_name
= "*NetBSD newfs";
678 context
.app_version_main
= APP_VERSION_MAIN
;
679 context
.app_version_sub
= APP_VERSION_SUB
;
680 context
.impl_name
= IMPL_NAME
;
682 /* minimum and maximum UDF versions we advise */
683 context
.min_udf
= 0x201;
684 context
.max_udf
= 0x201;
686 /* use user's time zone as default */
688 tm
= localtime(&now
);
689 context
.gmtoff
= tm
->tm_gmtoff
;
691 /* process options */
692 while ((ch
= getopt(argc
, argv
, "cFL:Mp:P:s:S:B:t:v:V:")) != -1) {
701 if (context
.logvol_name
) free(context
.logvol_name
);
702 context
.logvol_name
= strdup(optarg
);
705 req_disable
|= FORMAT_META
;
708 meta_perc
= a_num(optarg
, "meta_perc");
709 /* limit to `sensible` values */
710 meta_perc
= MIN(meta_perc
, 99);
711 meta_perc
= MAX(meta_perc
, 1);
712 meta_fract
= (float) meta_perc
/100.0;
715 context
.min_udf
= a_udf_version(optarg
, "min_udf");
716 if (context
.min_udf
> context
.max_udf
)
717 context
.max_udf
= context
.min_udf
;
720 context
.max_udf
= a_udf_version(optarg
, "max_udf");
721 if (context
.min_udf
> context
.max_udf
)
722 context
.min_udf
= context
.max_udf
;
725 /* check if there is a ':' in the name */
726 if ((colon
= strstr(optarg
, ":"))) {
727 if (context
.volset_name
)
728 free(context
.volset_name
);
730 context
.volset_name
= strdup(optarg
);
733 if (context
.primary_name
)
734 free(context
.primary_name
);
735 if ((strstr(optarg
, ":"))) {
736 perror("primary name can't have ':' in its name");
739 context
.primary_name
= strdup(optarg
);
742 /* support for files, set file size */
743 /* XXX support for formatting recordables on vnd/file? */
744 if (dehumanize_number(optarg
, &setsize
) < 0) {
745 perror("can't parse size argument");
748 setsize
= MAX(0, setsize
);
751 imagefile_secsize
= a_num(optarg
, "secsize");
752 imagefile_secsize
= MAX(512, imagefile_secsize
);
755 emul_packetsize
= a_num(optarg
,
756 "blockingnr, packetsize");
757 emul_packetsize
= MAX(emul_packetsize
, 1);
758 emul_packetsize
= MIN(emul_packetsize
, 32);
761 /* time zone overide */
762 context
.gmtoff
= a_num(optarg
, "gmtoff");
770 if (optind
+ 1 != argc
)
773 /* get device and directory specifier */
777 if ((fd
= open(dev
, O_RDWR
, 0)) == -1) {
778 /* check if we need to create a file */
779 fd
= open(dev
, O_RDONLY
, 0);
781 perror("device is there but can't be opened for read/write");
785 perror("can't open device");
789 perror("need to create image file but no size specified");
792 /* need to create a file */
793 fd
= open(dev
, O_RDWR
| O_CREAT
| O_TRUNC
, 0777);
795 perror("can't create image file");
800 /* stat the device */
801 if (fstat(fd
, &st
) != 0) {
802 perror("can't stat the device");
807 if (S_ISREG(st
.st_mode
)) {
809 setsize
= st
.st_size
;
810 /* sanitise arguments */
811 imagefile_secsize
&= ~511;
812 setsize
&= ~(imagefile_secsize
-1);
814 if (ftruncate(fd
, setsize
)) {
815 perror("can't resize file");
820 /* formatting can only be done on raw devices */
821 if (!S_ISREG(st
.st_mode
) && !S_ISCHR(st
.st_mode
)) {
822 printf("%s is not a raw device\n", dev
);
827 /* just in case something went wrong, synchronise the drive's cache */
828 udf_synchronise_caches();
830 /* get 'disc' information */
831 error
= udf_update_discinfo(&mmc_discinfo
);
833 perror("can't retrieve discinfo");
838 /* derive disc identifiers when not specified and check given */
839 error
= udf_proces_names();
841 /* error message has been printed */
846 /* derive newfs disc format from disc profile */
847 error
= udf_derive_format(req_enable
, req_disable
, force
);
849 /* error message has been printed */
854 udf_dump_discinfo(&mmc_discinfo
);
855 printf("Formatting disc compatible with UDF version %x to %x\n\n",
856 context
.min_udf
, context
.max_udf
);
857 (void)snprintb(scrap
, sizeof(scrap
), FORMAT_FLAGBITS
,
858 (uint64_t) format_flags
);
859 printf("UDF properties %s\n", scrap
);
860 printf("Volume set `%s'\n", context
.volset_name
);
861 printf("Primary volume `%s`\n", context
.primary_name
);
862 printf("Logical volume `%s`\n", context
.logvol_name
);
863 if (format_flags
& FORMAT_META
)
864 printf("Metadata percentage %d %%\n", meta_perc
);
867 /* prepare disc if nessisary (recordables mainly) */
868 error
= udf_prepare_disc();
870 perror("preparing disc failed");
875 /* setup sector writeout queue's */
876 TAILQ_INIT(&write_queue
);
878 /* perform the newfs itself */
879 error
= udf_do_newfs();
881 /* write out sectors */
882 error
= writeout_write_queue();
885 /* in any case, synchronise the drive's cache to prevent lockups */
886 udf_synchronise_caches();
895 /* --------------------------------------------------------------------- */