1 /*- 2 * Copyright (c) 2002, 2003 Gordon Tetlow 3 * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 36 #include <ufs/ufs/dinode.h> 37 #include <ufs/ffs/fs.h> 38 39 #include <geom/geom.h> 40 #include <geom/label/g_label.h> 41 42 #define G_LABEL_UFS_VOLUME_DIR "ufs" 43 #define G_LABEL_UFS_ID_DIR "ufsid" 44 45 #define G_LABEL_UFS_VOLUME 0 46 #define G_LABEL_UFS_ID 1 47 48 /* 49 * G_LABEL_UFS_CMP returns true if difference between provider mediasize 50 * and filesystem size is less than G_LABEL_UFS_MAXDIFF sectors 51 */ 52 #define G_LABEL_UFS_CMP(prov, fsys, size) \ 53 ( abs( ((fsys)->size) - ( (prov)->mediasize / (fsys)->fs_fsize )) \ 54 < G_LABEL_UFS_MAXDIFF ) 55 #define G_LABEL_UFS_MAXDIFF 0x100 56 57 static const int superblocks[] = SBLOCKSEARCH; 58 59 static void 60 g_label_ufs_taste_common(struct g_consumer *cp, char *label, size_t size, int what) 61 { 62 struct g_provider *pp; 63 int sb, superblock; 64 struct fs *fs; 65 66 g_topology_assert_not(); 67 pp = cp->provider; 68 label[0] = '\0'; 69 70 if (SBLOCKSIZE % cp->provider->sectorsize != 0) 71 return; 72 73 /* 74 * Walk through the standard places that superblocks hide and look 75 * for UFS magic. If we find magic, then check that the size in the 76 * superblock corresponds to the size of the underlying provider. 77 * Finally, look for a volume label and create an appropriate 78 * provider based on that. 79 */ 80 for (sb = 0; (superblock = superblocks[sb]) != -1; sb++) { 81 /* 82 * Take care not to issue an invalid I/O request. The offset of 83 * the superblock candidate must be multiples of the provider's 84 * sector size, otherwise an FFS can't exist on the provider 85 * anyway. 86 */ 87 if (superblock % cp->provider->sectorsize != 0) 88 continue; 89 90 fs = (struct fs *)g_read_data(cp, superblock, SBLOCKSIZE, NULL); 91 if (fs == NULL) 92 continue; 93 /* 94 * Check for magic. We also need to check if file system size 95 * is almost equal to providers size, because sysinstall(8) 96 * used to bogusly put first partition at offset 0 97 * instead of 16, and glabel/ufs would find file system on slice 98 * instead of partition. 99 * 100 * In addition, media size can be a bit bigger than file system 101 * size. For instance, mkuzip can append bytes to align data 102 * to large sector size (it improves compression rates). 103 */ 104 switch (fs->fs_magic){ 105 case FS_UFS1_MAGIC: 106 case FS_UFS2_MAGIC: 107 G_LABEL_DEBUG(1, "%s %s params: %jd, %d, %d, %jd\n", 108 fs->fs_magic == FS_UFS1_MAGIC ? "UFS1" : "UFS2", 109 pp->name, pp->mediasize, fs->fs_fsize, 110 fs->fs_old_size, fs->fs_providersize); 111 break; 112 default: 113 break; 114 } 115 116 if (fs->fs_magic == FS_UFS1_MAGIC && fs->fs_fsize > 0 && 117 ( G_LABEL_UFS_CMP(pp, fs, fs_old_size) 118 || G_LABEL_UFS_CMP(pp, fs, fs_providersize))) { 119 /* Valid UFS1. */ 120 } else if (fs->fs_magic == FS_UFS2_MAGIC && fs->fs_fsize > 0 && 121 ( G_LABEL_UFS_CMP(pp, fs, fs_size) 122 || G_LABEL_UFS_CMP(pp, fs, fs_providersize))) { 123 /* Valid UFS2. */ 124 } else { 125 g_free(fs); 126 continue; 127 } 128 if (fs->fs_sblockloc != superblock || fs->fs_ncg < 1 || 129 fs->fs_bsize < MINBSIZE || 130 fs->fs_bsize < sizeof(struct fs)) { 131 g_free(fs); 132 continue; 133 } 134 G_LABEL_DEBUG(1, "%s file system detected on %s.", 135 fs->fs_magic == FS_UFS1_MAGIC ? "UFS1" : "UFS2", pp->name); 136 switch (what) { 137 case G_LABEL_UFS_VOLUME: 138 /* Check for volume label */ 139 if (fs->fs_volname[0] == '\0') { 140 g_free(fs); 141 continue; 142 } 143 strlcpy(label, fs->fs_volname, size); 144 break; 145 case G_LABEL_UFS_ID: 146 if (fs->fs_id[0] == 0 && fs->fs_id[1] == 0) { 147 g_free(fs); 148 continue; 149 } 150 snprintf(label, size, "%08x%08x", fs->fs_id[0], 151 fs->fs_id[1]); 152 break; 153 } 154 g_free(fs); 155 break; 156 } 157 } 158 159 static void 160 g_label_ufs_volume_taste(struct g_consumer *cp, char *label, size_t size) 161 { 162 163 g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_VOLUME); 164 } 165 166 static void 167 g_label_ufs_id_taste(struct g_consumer *cp, char *label, size_t size) 168 { 169 170 g_label_ufs_taste_common(cp, label, size, G_LABEL_UFS_ID); 171 } 172 173 struct g_label_desc g_label_ufs_volume = { 174 .ld_taste = g_label_ufs_volume_taste, 175 .ld_dir = G_LABEL_UFS_VOLUME_DIR, 176 .ld_enabled = 1 177 }; 178 179 struct g_label_desc g_label_ufs_id = { 180 .ld_taste = g_label_ufs_id_taste, 181 .ld_dir = G_LABEL_UFS_ID_DIR, 182 .ld_enabled = 1 183 }; 184 185 G_LABEL_INIT(ufsid, g_label_ufs_id, "Create device nodes for UFS file system IDs"); 186 G_LABEL_INIT(ufs, g_label_ufs_volume, "Create device nodes for UFS volume names"); 187