1/*-
2 * Copyright (c) 2005 Takanori Watanabe
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34
35#include <geom/geom.h>
36#include <geom/label/g_label.h>
37
38#define	NTFS_A_VOLUMENAME	0x60
39#define	NTFS_FILEMAGIC		((uint32_t)(0x454C4946))
40#define	NTFS_VOLUMEINO		3
41
42#define G_LABEL_NTFS_DIR	"ntfs"
43
44struct ntfs_attr {
45	uint32_t	a_type;
46	uint32_t	reclen;
47	uint8_t		a_flag;
48	uint8_t		a_namelen;
49	uint8_t		a_nameoff;
50	uint8_t		reserved1;
51	uint8_t		a_compression;
52	uint8_t		reserved2;
53	uint16_t	a_index;
54	uint16_t	a_datalen;
55	uint16_t	reserved3;
56	uint16_t	a_dataoff;
57	uint16_t	a_indexed;
58} __packed;
59
60struct ntfs_filerec {
61	uint32_t	fr_hdrmagic;
62	uint16_t	fr_hdrfoff;
63	uint16_t	fr_hdrfnum;
64	uint8_t		reserved[8];
65	uint16_t	fr_seqnum;
66	uint16_t	fr_nlink;
67	uint16_t	fr_attroff;
68	uint16_t	fr_flags;
69	uint32_t	fr_size;
70	uint32_t	fr_allocated;
71	uint64_t	fr_mainrec;
72	uint16_t	fr_attrnum;
73} __packed;
74
75struct ntfs_bootfile {
76	uint8_t		reserved1[3];
77	uint8_t		bf_sysid[8];
78	uint16_t	bf_bps;
79	uint8_t		bf_spc;
80	uint8_t		reserved2[7];
81	uint8_t		bf_media;
82	uint8_t		reserved3[2];
83	uint16_t	bf_spt;
84	uint16_t	bf_heads;
85	uint8_t		reserver4[12];
86	uint64_t	bf_spv;
87	uint64_t	bf_mftcn;
88	uint64_t	bf_mftmirrcn;
89	int8_t		bf_mftrecsz;
90	uint32_t	bf_ibsz;
91	uint32_t	bf_volsn;
92} __packed;
93
94static void
95g_label_ntfs_taste(struct g_consumer *cp, char *label, size_t size)
96{
97	struct g_provider *pp;
98	struct ntfs_bootfile *bf;
99	struct ntfs_filerec *fr;
100	struct ntfs_attr *atr;
101	off_t voloff;
102	char *filerecp, *ap;
103	int8_t mftrecsz;
104	char vnchar;
105	int recsize, j;
106
107	g_topology_assert_not();
108
109	label[0] = '\0';
110	pp = cp->provider;
111	filerecp = NULL;
112
113	bf = (struct ntfs_bootfile *)g_read_data(cp, 0, pp->sectorsize, NULL);
114	if (bf == NULL || strncmp(bf->bf_sysid, "NTFS    ", 8) != 0)
115		goto done;
116
117	mftrecsz = bf->bf_mftrecsz;
118	recsize = (mftrecsz > 0) ? (mftrecsz * bf->bf_bps * bf->bf_spc) : (1 << -mftrecsz);
119	if (recsize == 0 || recsize % pp->sectorsize != 0)
120		goto done;
121
122	voloff = bf->bf_mftcn * bf->bf_spc * bf->bf_bps +
123	    recsize * NTFS_VOLUMEINO;
124	if (voloff % pp->sectorsize != 0)
125		goto done;
126
127	filerecp = g_read_data(cp, voloff, recsize, NULL);
128	if (filerecp == NULL)
129		goto done;
130	fr = (struct ntfs_filerec *)filerecp;
131
132	if (fr->fr_hdrmagic != NTFS_FILEMAGIC)
133		goto done;
134
135	for (ap = filerecp + fr->fr_attroff;
136	    atr = (struct ntfs_attr *)ap, atr->a_type != -1;
137	    ap += atr->reclen) {
138		if (atr->a_type == NTFS_A_VOLUMENAME) {
139			if(atr->a_datalen >= size *2){
140				label[0] = 0;
141				goto done;
142			}
143			/*
144			 *UNICODE to ASCII.
145			 * Should we need to use iconv(9)?
146			 */
147			for (j = 0; j < atr->a_datalen; j++) {
148				vnchar = *(ap + atr->a_dataoff + j);
149				if (j & 1) {
150					if (vnchar) {
151						label[0] = 0;
152						goto done;
153					}
154				} else {
155					label[j / 2] = vnchar;
156				}
157			}
158			label[j / 2] = 0;
159			break;
160		}
161	}
162done:
163	if (bf != NULL)
164		g_free(bf);
165	if (filerecp != NULL)
166		g_free(filerecp);
167}
168
169struct g_label_desc g_label_ntfs = {
170	.ld_taste = g_label_ntfs_taste,
171	.ld_dir = G_LABEL_NTFS_DIR,
172	.ld_enabled = 1
173};
174
175G_LABEL_INIT(ntfs, g_label_ntfs, "Create device nodes for NTFS volumes");
176