1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2002, 2003 Gordon Tetlow
5 * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/disklabel.h>
33 #include <sys/malloc.h>
34 #include <sys/vnode.h>
35
36 #include <ufs/ufs/dinode.h>
37 #include <ufs/ffs/fs.h>
38 #include <ufs/ufs/quota.h>
39 #include <ufs/ufs/extattr.h>
40 #include <ufs/ffs/ffs_extern.h>
41
42 #include <geom/geom.h>
43 #include <geom/geom_dbg.h>
44 #include <geom/label/g_label.h>
45
46 #define G_LABEL_UFS_VOLUME 0
47 #define G_LABEL_UFS_ID 1
48
49 /*
50 * G_LABEL_UFS_CMP returns true if difference between provider mediasize
51 * and filesystem size is less than G_LABEL_UFS_MAXDIFF sectors
52 */
53 #define G_LABEL_UFS_CMP(prov, fsys, size) \
54 ( abs( ((fsys)->size) - ( (prov)->mediasize / (fsys)->fs_fsize )) \
55 < G_LABEL_UFS_MAXDIFF )
56 #define G_LABEL_UFS_MAXDIFF 0x100
57
58 /*
59 * For providers that look like disklabels we need to check if the file system
60 * size is almost equal to the provider's size, because sysinstall(8) used to
61 * bogusly put the first partition at offset 0 instead of 16, and glabel/ufs
62 * would find a file system on the slice instead of the partition.
63 *
64 * In addition, media size can be a bit bigger than file system size. For
65 * instance, mkuzip can append bytes to align data to large sector size (it
66 * improves compression rates).
67 */
68 static bool
g_label_ufs_ignore_bsdlabel_slice(struct g_consumer * cp,struct fs * fs)69 g_label_ufs_ignore_bsdlabel_slice(struct g_consumer *cp,
70 struct fs *fs)
71 {
72 struct g_provider *pp;
73 u_char *buf;
74 uint32_t magic1, magic2;
75 int error;
76
77 pp = cp->provider;
78
79 /*
80 * If the expected provider size for the filesystem matches the
81 * real provider size then don't ignore this filesystem.
82 */
83 if (G_LABEL_UFS_CMP(pp, fs, fs_providersize))
84 return (false);
85
86 /*
87 * If the filesystem size matches the real provider size then
88 * don't ignore this filesystem.
89 */
90 if (fs->fs_magic == FS_UFS1_MAGIC ?
91 G_LABEL_UFS_CMP(pp, fs, fs_old_size) :
92 G_LABEL_UFS_CMP(pp, fs, fs_size))
93 return (false);
94
95 /*
96 * Provider is bigger than expected; probe to see if there's a
97 * disklabel. Adapted from g_part_bsd_probe.
98 */
99
100 /* Check if the superblock overlaps where the disklabel lives. */
101 if (fs->fs_sblockloc < pp->sectorsize * 2)
102 return (false);
103
104 /* Sanity-check the provider. */
105 if (pp->sectorsize < sizeof(struct disklabel) ||
106 pp->mediasize < BBSIZE)
107 return (false);
108 if (BBSIZE % pp->sectorsize)
109 return (false);
110
111 /* Check that there's a disklabel. */
112 buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
113 if (buf == NULL)
114 return (false);
115 magic1 = le32dec(buf + 0);
116 magic2 = le32dec(buf + 132);
117 g_free(buf);
118 if (magic1 == DISKMAGIC && magic2 == DISKMAGIC)
119 return (true);
120
121 return (false);
122 }
123
124 /*
125 * Try to find a superblock on the provider. If successful, look for a volume
126 * label and create an appropriate provider based on that.
127 */
128 static void
g_label_ufs_taste_common(struct g_consumer * cp,char * label,size_t size,int what)129 g_label_ufs_taste_common(struct g_consumer *cp, char *label, size_t size, int what)
130 {
131 struct g_provider *pp;
132 struct fs *fs;
133
134 g_topology_assert_not();
135 pp = cp->provider;
136 label[0] = '\0';
137
138 fs = NULL;
139 KASSERT(pp->sectorsize != 0, ("Tasting a disk with 0 sectorsize"));
140 if (SBLOCKSIZE % pp->sectorsize != 0 || ffs_sbget(cp, &fs, UFS_STDSB,
141 UFS_NOHASHFAIL | UFS_NOCSUM | UFS_NOMSG, M_GEOM, g_use_g_read_data)
142 != 0) {
143 KASSERT(fs == NULL,
144 ("g_label_ufs_taste_common: non-NULL fs %p\n", fs));
145 return;
146 }
147
148 /* Check for magic. */
149 if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_fsize > 0) {
150 /* Valid UFS1. */
151 } else if (fs->fs_magic == FS_UFS2_MAGIC && fs->fs_fsize > 0) {
152 /* Valid UFS2. */
153 } else {
154 goto out;
155 }
156 /* Check if this should be ignored for compatibility. */
157 if (g_label_ufs_ignore_bsdlabel_slice(cp, fs))
158 goto out;
159 G_LABEL_DEBUG(1, "%s file system detected on %s.",
160 fs->fs_magic == FS_UFS1_MAGIC ? "UFS1" : "UFS2", pp->name);
161 switch (what) {
162 case G_LABEL_UFS_VOLUME:
163 /* Check for volume label */
164 if (fs->fs_volname[0] != '\0')
165 strlcpy(label, fs->fs_volname, size);
166 break;
167 case G_LABEL_UFS_ID:
168 if (fs->fs_id[0] != 0 || fs->fs_id[1] != 0)
169 snprintf(label, size, "%08x%08x", fs->fs_id[0],
170 fs->fs_id[1]);
171 break;
172 }
173 out:
174 g_free(fs);
175 }
176
177 static void
g_label_ufs_volume_taste(struct g_consumer * cp,char * label,size_t size)178 g_label_ufs_volume_taste(struct g_consumer *cp, char *label, size_t size)
179 {
180
181 g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_VOLUME);
182 }
183
184 static void
g_label_ufs_id_taste(struct g_consumer * cp,char * label,size_t size)185 g_label_ufs_id_taste(struct g_consumer *cp, char *label, size_t size)
186 {
187
188 g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_ID);
189 }
190
191 struct g_label_desc g_label_ufs_volume = {
192 .ld_taste = g_label_ufs_volume_taste,
193 .ld_dirprefix = "ufs/",
194 .ld_enabled = 1
195 };
196
197 struct g_label_desc g_label_ufs_id = {
198 .ld_taste = g_label_ufs_id_taste,
199 .ld_dirprefix = "ufsid/",
200 .ld_enabled = 1
201 };
202
203 G_LABEL_INIT(ufsid, g_label_ufs_id, "Create device nodes for UFS file system IDs");
204 G_LABEL_INIT(ufs, g_label_ufs_volume, "Create device nodes for UFS volume names");
205
206 MODULE_DEPEND(g_label, ufs, 1, 1, 1);
207