1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Label a file system volume.
30 */
31
32
33#include <stdio.h>
34#include <string.h>
35#include <strings.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <fcntl.h>
39#include <locale.h>
40#include <errno.h>
41#include <sys/fcntl.h>
42#include <sys/param.h>
43#include <sys/types.h>
44#include <sys/mntent.h>
45
46#include <sys/fs/udf_volume.h>
47#include "ud_lib.h"
48
49static uint8_t buf[MAXBSIZE];
50static uint64_t off;
51#define	BUF_LEN	0x200
52static int8_t	lvinfo1_buf[BUF_LEN];
53static int8_t	lvinfo2_buf[BUF_LEN];
54static int8_t	lvinfo3_buf[BUF_LEN];
55static int8_t	fsname[BUF_LEN];
56static int8_t	volname[BUF_LEN];
57static int32_t fsname_len;
58
59#define	SET_LVINFO1	0x01
60#define	SET_LVINFO2	0x02
61#define	SET_LVINFO3	0x04
62#define	SET_FSNAME	0x08
63#define	SET_VOLNAME	0x10
64
65typedef unsigned short unicode_t;
66
67#define	FSNAME_STR_LEN	(8 + 2)
68#define	VOLNAME_STR_LEN	32
69#define	INFO_STR_LEN	36
70
71static void usage();
72static void label(ud_handle_t, uint32_t);
73static void print_info(struct vds *, char *, ud_handle_t);
74static void label_vds(struct vds *, uint32_t, ud_handle_t);
75static int32_t convert_string(int8_t *, int8_t *, int32_t, int32_t, int8_t *);
76static int32_t ud_convert2unicode(int8_t *, int8_t *, int32_t);
77
78
79int8_t *labelit_subopts[] = {
80#define	LVINFO1	0x00
81	"lvinfo1",
82#define	LVINFO2	0x01
83	"lvinfo2",
84#define	LVINFO3	0x02
85	"lvinfo3",
86	NULL};
87
88
89int
90main(int32_t argc, char *argv[])
91{
92	int32_t		opt = 0;
93	int32_t		flags = 0;
94	int32_t		ret = 0;
95	int8_t		*options = NULL;
96	int8_t		*value = NULL;
97	uint32_t	set_flags = 0;
98	ud_handle_t	udh;
99
100	(void) setlocale(LC_ALL, "");
101
102#if !defined(TEXT_DOMAIN)
103#define	TEXT_DOMAIN	"SYS_TEST"
104#endif
105
106	(void) textdomain(TEXT_DOMAIN);
107
108
109	while ((opt = getopt(argc, argv, "F:o:")) != EOF) {
110		switch (opt) {
111		case 'F':
112			if (strcmp(optarg, "udfs") != 0) {
113				usage();
114			}
115			break;
116
117		case 'o':
118			/*
119			 * UDFS specific options
120			 */
121			options = optarg;
122			while (*options != '\0') {
123				switch (getsubopt(&options, labelit_subopts,
124						&value)) {
125				case LVINFO1 :
126					set_flags |= SET_LVINFO1;
127					(void) convert_string(value,
128						lvinfo1_buf, BUF_LEN,
129						INFO_STR_LEN,
130			gettext("udfs labelit: lvinfo1 should be less than "
131			"36 bytes after converting to compressed unicode "
132			"dstring\n"));
133					break;
134				case LVINFO2 :
135					set_flags |= SET_LVINFO2;
136					(void) convert_string(value,
137						lvinfo2_buf, BUF_LEN,
138						INFO_STR_LEN,
139			gettext("udfs labelit: lvinfo2 should be less than "
140			"36 bytes after converting to compressed unicode "
141			"dstring\n"));
142					break;
143				case LVINFO3 :
144					set_flags |= SET_LVINFO3;
145					(void) convert_string(value,
146						lvinfo3_buf, BUF_LEN,
147						INFO_STR_LEN,
148			gettext("udfs labelit: lvinfo3 should be less than "
149			"36 bytes after converting to compressed unicode "
150			"dstring\n"));
151					break;
152				default:
153					(void) fprintf(stderr,
154			gettext("udfs labelit: Unknown suboption %s\n"), value);
155					usage();
156					break;
157				}
158			}
159			break;
160
161		case '?':
162			usage();
163		}
164	}
165
166	if ((argc - optind) == 3) {
167
168		/*
169		 * There are restrictions on the
170		 * length of the names
171		 * fsname is 8 characters
172		 * volume name is 32 characters
173		 * The extra byte is for compression id
174		 */
175		fsname_len = convert_string(argv[optind + 1],
176				fsname, BUF_LEN, FSNAME_STR_LEN,
177	gettext("udfs labelit: fsname can not be longer than 8 characters\n"));
178
179		(void) convert_string(argv[optind + 2],
180				volname, BUF_LEN, VOLNAME_STR_LEN,
181		gettext("udfs labelit: volname can not be longer "
182			"than 32 bytes after converting to "
183			"compressed unicode dstring\n"));
184		set_flags |= SET_FSNAME | SET_VOLNAME;
185	} else {
186		if ((argc - optind) != 1) {
187			usage();
188		}
189	}
190
191	if (ud_init(-1, &udh) != 0) {
192		(void) fprintf(stderr,
193		gettext("udfs labelit: cannot initialize ud_lib\n"));
194		exit(1);
195	}
196
197	/*
198	 * Open special device
199	 */
200	if (set_flags == 0) {
201		flags = O_RDONLY;
202	} else {
203		flags = O_RDWR;
204	}
205	if (ud_open_dev(udh, argv[optind], flags) != 0) {
206		(void) fprintf(stderr,
207		gettext("udfs labelit: cannot open <%s> errorno <%d>\n"),
208					argv[optind], errno);
209		exit(1);
210	}
211
212	if ((ret = ud_fill_udfs_info(udh)) != 0) {
213		goto close_dev;
214	}
215
216	if ((udh->udfs.flags & VALID_UDFS) == 0) {
217		ret = 1;
218		goto close_dev;
219	}
220
221	label(udh, set_flags);
222
223close_dev:
224	ud_close_dev(udh);
225	ud_fini(udh);
226
227	return (ret);
228}
229
230static void
231usage()
232{
233	(void) fprintf(stderr, gettext(
234		"udfs usage: labelit [-F udfs] [generic options] "
235		"[ -o specific_options ] special [fsname volume]\n"));
236	(void) fprintf(stderr, gettext(
237		" -o : specific_options : [lvinfo1=string],"
238		"[lvinfo2=string],[lvinfo3=string]\n"));
239	(void) fprintf(stderr,
240		gettext("NOTE that all -o suboptions: must"
241		" be separated only by commas.\n"));
242	exit(1);
243}
244
245static void
246label(ud_handle_t udh, uint32_t set_flags)
247{
248	if (set_flags == 0) {
249		if (udh->udfs.flags & VALID_MVDS) {
250			print_info(&udh->udfs.mvds, "mvds", udh);
251		}
252		if (udh->udfs.flags & VALID_RVDS) {
253			print_info(&udh->udfs.rvds, "rvds", udh);
254		}
255		return;
256	} else {
257
258		if (udh->udfs.flags & VALID_MVDS) {
259			label_vds(&udh->udfs.mvds, set_flags, udh);
260		}
261		if (udh->udfs.flags & VALID_RVDS) {
262			label_vds(&udh->udfs.rvds, set_flags, udh);
263		}
264		if (((set_flags & (SET_FSNAME | SET_VOLNAME)) ==
265			(SET_FSNAME | SET_VOLNAME)) &&
266			(udh->udfs.fsd_len != 0)) {
267			struct file_set_desc *fsd;
268
269			off = udh->udfs.fsd_loc * udh->udfs.lbsize;
270			if (ud_read_dev(udh, off, buf,
271				udh->udfs.fsd_len) != 0) {
272				return;
273			}
274
275			/* LINTED */
276			fsd = (struct file_set_desc *)buf;
277
278			set_dstring(fsd->fsd_lvid,
279				volname, sizeof (fsd->fsd_lvid));
280			set_dstring(fsd->fsd_fsi,
281				volname, sizeof (fsd->fsd_fsi));
282
283			ud_make_tag(udh, &fsd->fsd_tag, UD_FILE_SET_DESC,
284				SWAP_32(fsd->fsd_tag.tag_loc),
285				SWAP_16(fsd->fsd_tag.tag_crc_len));
286
287			(void) ud_write_dev(udh, off, buf, udh->udfs.fsd_len);
288		}
289	}
290}
291
292static void
293print_info(struct vds *v, char *name, ud_handle_t udh)
294{
295	uint8_t		outbuf[BUF_LEN];
296
297	if (v->pvd_len != 0) {
298		off = v->pvd_loc * udh->udfs.lbsize;
299		if (ud_read_dev(udh, off, buf,
300			sizeof (struct pri_vol_desc)) == 0) {
301
302			struct pri_vol_desc *pvd;
303
304			/* LINTED */
305			pvd = (struct pri_vol_desc *)buf;
306
307			bzero(outbuf, BUF_LEN);
308			(void) ud_convert2local(
309					(int8_t *)pvd->pvd_vsi,
310					(int8_t *)outbuf, strlen(pvd->pvd_vsi));
311			(void) fprintf(stdout,
312				gettext("fsname in  %s : %s\n"),
313					name, outbuf);
314
315			bzero(outbuf, BUF_LEN);
316			pvd->pvd_vol_id[31] = '\0';
317			(void) ud_convert2local(
318					(int8_t *)pvd->pvd_vol_id,
319					(int8_t *)outbuf,
320					strlen(pvd->pvd_vol_id));
321			(void) fprintf(stdout,
322				gettext("volume label in %s : %s\n"),
323					name, outbuf);
324		}
325	}
326
327	if (v->iud_len != 0) {
328		off = v->iud_loc * udh->udfs.lbsize;
329		if (ud_read_dev(udh, off, buf,
330			sizeof (struct iuvd_desc)) == 0) {
331
332			struct iuvd_desc *iud;
333
334			/* LINTED */
335			iud = (struct iuvd_desc *)buf;
336			bzero(outbuf, BUF_LEN);
337			iud->iuvd_ifo1[35] = '\0';
338			(void) ud_convert2local(
339					(int8_t *)iud->iuvd_ifo1,
340					(int8_t *)outbuf,
341					strlen(iud->iuvd_ifo1));
342			(void) fprintf(stdout,
343				gettext("LVInfo1 in  %s : %s\n"),
344					name, outbuf);
345
346			bzero(outbuf, BUF_LEN);
347			iud->iuvd_ifo2[35] = '\0';
348			(void) ud_convert2local(
349					(int8_t *)iud->iuvd_ifo2,
350					(int8_t *)outbuf,
351					strlen(iud->iuvd_ifo2));
352			(void) fprintf(stdout,
353				gettext("LVInfo2 in  %s : %s\n"),
354					name, outbuf);
355
356			bzero(outbuf, BUF_LEN);
357			iud->iuvd_ifo3[35] = '\0';
358			(void) ud_convert2local(
359					(int8_t *)iud->iuvd_ifo3,
360					(int8_t *)outbuf,
361					strlen(iud->iuvd_ifo3));
362			(void) fprintf(stdout,
363				gettext("LVInfo3 in  %s : %s\n"),
364					name, outbuf);
365		}
366	}
367}
368
369/* ARGSUSED */
370static void
371label_vds(struct vds *v, uint32_t set_flags, ud_handle_t udh)
372{
373
374	if (((set_flags & (SET_FSNAME | SET_VOLNAME)) ==
375		(SET_FSNAME | SET_VOLNAME)) &&
376		(v->pvd_len)) {
377
378		off = v->pvd_loc * udh->udfs.lbsize;
379		if (ud_read_dev(udh, off, buf,
380			sizeof (struct pri_vol_desc)) == 0) {
381
382			struct pri_vol_desc *pvd;
383
384			/* LINTED */
385			pvd = (struct pri_vol_desc *)buf;
386			bzero((int8_t *)&pvd->pvd_vsi[9], 119);
387			(void) strncpy((int8_t *)&pvd->pvd_vsi[9],
388					&fsname[1], fsname_len - 1);
389
390			set_dstring(pvd->pvd_vol_id,
391				volname, sizeof (pvd->pvd_vol_id));
392
393			ud_make_tag(udh, &pvd->pvd_tag,
394				SWAP_16(pvd->pvd_tag.tag_id),
395				SWAP_32(pvd->pvd_tag.tag_loc),
396				SWAP_16(pvd->pvd_tag.tag_crc_len));
397
398			(void) ud_write_dev(udh, off, buf,
399				sizeof (struct pri_vol_desc));
400		}
401	}
402
403	if (set_flags && v->iud_len) {
404
405		off = v->iud_loc * udh->udfs.lbsize;
406		if (ud_read_dev(udh, off, buf,
407			sizeof (struct iuvd_desc)) == 0) {
408
409			struct iuvd_desc *iuvd;
410
411			/* LINTED */
412			iuvd = (struct iuvd_desc *)buf;
413
414			if ((set_flags & SET_VOLNAME) == SET_VOLNAME) {
415				set_dstring(iuvd->iuvd_lvi,
416					volname, sizeof (iuvd->iuvd_lvi));
417			}
418			if ((set_flags & SET_LVINFO1) == SET_LVINFO1) {
419				set_dstring(iuvd->iuvd_ifo1,
420					lvinfo1_buf, sizeof (iuvd->iuvd_ifo1));
421			}
422			if ((set_flags & SET_LVINFO2) == SET_LVINFO2) {
423				set_dstring(iuvd->iuvd_ifo2,
424					lvinfo2_buf, sizeof (iuvd->iuvd_ifo2));
425			}
426			if ((set_flags & SET_LVINFO3) == SET_LVINFO3) {
427				set_dstring(iuvd->iuvd_ifo3,
428					lvinfo3_buf, sizeof (iuvd->iuvd_ifo3));
429			}
430
431			ud_make_tag(udh, &iuvd->iuvd_tag,
432				SWAP_16(iuvd->iuvd_tag.tag_id),
433				SWAP_32(iuvd->iuvd_tag.tag_loc),
434				SWAP_16(iuvd->iuvd_tag.tag_crc_len));
435
436			(void) ud_write_dev(udh, off, buf,
437				sizeof (struct iuvd_desc));
438		}
439	}
440
441	if (((set_flags & (SET_FSNAME | SET_VOLNAME)) ==
442		(SET_FSNAME | SET_VOLNAME)) &&
443		(v->lvd_len)) {
444
445		off = v->lvd_loc * udh->udfs.lbsize;
446		if (ud_read_dev(udh, off, buf,
447			sizeof (struct log_vol_desc)) == 0) {
448
449			struct log_vol_desc *lvd;
450
451			/* LINTED */
452			lvd = (struct log_vol_desc *)buf;
453			set_dstring(lvd->lvd_lvid,
454				volname, sizeof (lvd->lvd_lvid));
455
456			ud_make_tag(udh, &lvd->lvd_tag,
457				SWAP_16(lvd->lvd_tag.tag_id),
458				SWAP_32(lvd->lvd_tag.tag_loc),
459				SWAP_16(lvd->lvd_tag.tag_crc_len));
460
461			(void) ud_write_dev(udh, off, buf,
462				sizeof (struct log_vol_desc));
463		}
464	}
465}
466
467
468int32_t
469convert_string(int8_t *value, int8_t *out_buf, int32_t out_len,
470	int32_t len, int8_t *error_string)
471{
472	int32_t		out_length = 0;
473
474	out_length = ud_convert2unicode(value, out_buf, out_len);
475	if (out_length > len - 1) {
476		(void) fprintf(stderr, "%s", error_string);
477		exit(1);
478	}
479
480	return (out_length);
481}
482
483static int32_t
484ud_convert2unicode(int8_t *mb, int8_t *comp, int32_t out_len)
485{
486	wchar_t		buf4c[128];
487	int32_t		len = 0;
488	int32_t		i = 0;
489	int32_t		j = 0;
490	uint8_t		c = 8;
491
492	len = mbstowcs(buf4c, mb, 127);
493	buf4c[127] = '\0';
494
495	for (i = 0; i < len; i++) {
496		if (buf4c[i] & 0xFFFFFF00) {
497			c = 16;
498			break;
499		}
500	}
501
502	comp[0] = c;
503	j = 1;
504	for (i = 0; i < len && i < out_len; i++) {
505		if (c == 16) {
506			comp[j] = (buf4c[i] & 0xFF00) >> 8;
507		}
508		comp[j++] = buf4c[i] & 0xFF;
509	}
510
511	return (j);
512}
513