xref: /netbsd-src/external/gpl2/lvm2/dist/lib/device/dev-md.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: dev-md.c,v 1.1.1.1 2008/12/22 00:17:57 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2004 Luca Berra
5  * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "lib.h"
19 #include "metadata.h"
20 #include "xlate.h"
21 #include "filter.h"
22 
23 #ifdef linux
24 
25 /* Lifted from <linux/raid/md_p.h> because of difficulty including it */
26 
27 #define MD_SB_MAGIC 0xa92b4efc
28 #define MD_RESERVED_BYTES (64 * 1024ULL)
29 #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512)
30 #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) \
31 				- MD_RESERVED_SECTORS)
32 
33 static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
34 {
35 	uint32_t md_magic;
36 
37 	/* Version 1 is little endian; version 0.90.0 is machine endian */
38 	if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
39 	    ((md_magic == xlate32(MD_SB_MAGIC)) ||
40 	     (md_magic == MD_SB_MAGIC)))
41 		return 1;
42 
43 	return 0;
44 }
45 
46 /*
47  * Calculate the position of the superblock.
48  * It is always aligned to a 4K boundary and
49  * depending on minor_version, it can be:
50  * 0: At least 8K, but less than 12K, from end of device
51  * 1: At start of device
52  * 2: 4K from start of device.
53  */
54 typedef enum {
55 	MD_MINOR_VERSION_MIN,
56 	MD_MINOR_V0 = MD_MINOR_VERSION_MIN,
57 	MD_MINOR_V1,
58 	MD_MINOR_V2,
59 	MD_MINOR_VERSION_MAX = MD_MINOR_V2
60 } md_minor_version_t;
61 
62 static uint64_t _v1_sb_offset(uint64_t size, md_minor_version_t minor_version)
63 {
64 	uint64_t uninitialized_var(sb_offset);
65 
66 	switch(minor_version) {
67 	case MD_MINOR_V0:
68 		sb_offset = (size - 8 * 2) & ~(4 * 2 - 1ULL);
69 		break;
70 	case MD_MINOR_V1:
71 		sb_offset = 0;
72 		break;
73 	case MD_MINOR_V2:
74 		sb_offset = 4 * 2;
75 		break;
76 	}
77 	sb_offset <<= SECTOR_SHIFT;
78 
79 	return sb_offset;
80 }
81 
82 /*
83  * Returns -1 on error
84  */
85 int dev_is_md(struct device *dev, uint64_t *sb)
86 {
87 	int ret = 1;
88 	md_minor_version_t minor;
89 	uint64_t size, sb_offset;
90 
91 	if (!dev_get_size(dev, &size)) {
92 		stack;
93 		return -1;
94 	}
95 
96 	if (size < MD_RESERVED_SECTORS * 2)
97 		return 0;
98 
99 	if (!dev_open(dev)) {
100 		stack;
101 		return -1;
102 	}
103 
104 	/* Check if it is an md component device. */
105 	/* Version 0.90.0 */
106 	sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
107 	if (_dev_has_md_magic(dev, sb_offset))
108 		goto out;
109 
110 	minor = MD_MINOR_VERSION_MIN;
111 	/* Version 1, try v1.0 -> v1.2 */
112 	do {
113 		sb_offset = _v1_sb_offset(size, minor);
114 		if (_dev_has_md_magic(dev, sb_offset))
115 			goto out;
116 	} while (++minor <= MD_MINOR_VERSION_MAX);
117 
118 	ret = 0;
119 
120 out:
121 	if (!dev_close(dev))
122 		stack;
123 
124 	if (ret && sb)
125 		*sb = sb_offset;
126 
127 	return ret;
128 }
129 
130 /*
131  * Retrieve chunk size from md device using sysfs.
132  */
133 unsigned long dev_md_chunk_size(const char *sysfs_dir, struct device *dev)
134 {
135 	char path[PATH_MAX+1], buffer[64];
136 	FILE *fp;
137 	struct stat info;
138 	unsigned long chunk_size_bytes = 0UL;
139 
140 	if (MAJOR(dev->dev) != md_major())
141 		return 0;
142 
143 	if (!sysfs_dir || !*sysfs_dir)
144 		return_0;
145 
146 	if (dm_snprintf(path, PATH_MAX, "%s/dev/block/%d:%d/md/chunk_size",
147 	    sysfs_dir, MAJOR(dev->dev), MINOR(dev->dev)) < 0) {
148 		log_error("dm_snprintf md chunk_size failed");
149 		return 0;
150 	}
151 
152 	/* old sysfs structure */
153 	if (stat(path, &info) &&
154 	    dm_snprintf(path, PATH_MAX, "%s/block/md%d/md/chunk_size",
155 			sysfs_dir, MINOR(dev->dev)) < 0) {
156 		log_error("dm_snprintf old md chunk size failed");
157 		return 0;
158 	}
159 
160 	if (!(fp = fopen(path, "r"))) {
161 		log_sys_error("fopen", path);
162 		return 0;
163 	}
164 
165 	if (!fgets(buffer, sizeof(buffer), fp)) {
166 		log_sys_error("fgets", path);
167 		goto out;
168 	}
169 
170 	if (sscanf(buffer, "%lu", &chunk_size_bytes) != 1) {
171 		log_error("sysfs file %s not in expected format: %s", path,
172 			  buffer);
173 		goto out;
174 	}
175 
176 	log_very_verbose("Device %s md chunk size is %lu bytes.",
177 			 dev_name(dev), chunk_size_bytes);
178 
179 out:
180 	if (fclose(fp))
181 		log_sys_error("fclose", path);
182 
183 	return chunk_size_bytes >> SECTOR_SHIFT;
184 }
185 
186 #else
187 
188 int dev_is_md(struct device *dev __attribute((unused)),
189 	      uint64_t *sb __attribute((unused)))
190 {
191 	return 0;
192 }
193 
194 unsigned long dev_md_chunk_size(const char *sysfs_dir __attribute((unused)),
195 				struct device *dev  __attribute((unused)))
196 {
197 	return 0UL;
198 }
199 
200 #endif
201