]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libblkid/src/superblocks/xfs.c
libblkid: (xfs) fix sector size calculation
[thirdparty/util-linux.git] / libblkid / src / superblocks / xfs.c
1 /*
2 * Copyright (C) 1999 by Andries Brouwer
3 * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
4 * Copyright (C) 2001 by Andreas Dilger
5 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
6 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
7 * Copyright (C) 2013 Eric Sandeen <sandeen@redhat.com>
8 *
9 * This file may be redistributed under the terms of the
10 * GNU Lesser General Public License.
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <ctype.h>
19 #include <stdint.h>
20
21 #include "superblocks.h"
22
23 struct xfs_super_block {
24 uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
25 uint32_t sb_blocksize; /* logical block size, bytes */
26 uint64_t sb_dblocks; /* number of data blocks */
27 uint64_t sb_rblocks; /* number of realtime blocks */
28 uint64_t sb_rextents; /* number of realtime extents */
29 unsigned char sb_uuid[16]; /* file system unique id */
30 uint64_t sb_logstart; /* starting block of log if internal */
31 uint64_t sb_rootino; /* root inode number */
32 uint64_t sb_rbmino; /* bitmap inode for realtime extents */
33 uint64_t sb_rsumino; /* summary inode for rt bitmap */
34 uint32_t sb_rextsize; /* realtime extent size, blocks */
35 uint32_t sb_agblocks; /* size of an allocation group */
36 uint32_t sb_agcount; /* number of allocation groups */
37 uint32_t sb_rbmblocks; /* number of rt bitmap blocks */
38 uint32_t sb_logblocks; /* number of log blocks */
39
40 uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */
41 uint16_t sb_sectsize; /* volume sector size, bytes */
42 uint16_t sb_inodesize; /* inode size, bytes */
43 uint16_t sb_inopblock; /* inodes per block */
44 char sb_fname[12]; /* file system name */
45 uint8_t sb_blocklog; /* log2 of sb_blocksize */
46 uint8_t sb_sectlog; /* log2 of sb_sectsize */
47 uint8_t sb_inodelog; /* log2 of sb_inodesize */
48 uint8_t sb_inopblog; /* log2 of sb_inopblock */
49 uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
50 uint8_t sb_rextslog; /* log2 of sb_rextents */
51 uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
52 uint8_t sb_imax_pct; /* max % of fs for inode space */
53 /* statistics */
54 uint64_t sb_icount; /* allocated inodes */
55 uint64_t sb_ifree; /* free inodes */
56 uint64_t sb_fdblocks; /* free data blocks */
57 uint64_t sb_frextents; /* free realtime extents */
58
59 /* this is not all... but enough for libblkid */
60
61 } __attribute__((packed));
62
63 #define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */
64 #define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */
65 #define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG)
66 #define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG)
67 #define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */
68 #define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */
69 #define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG)
70 #define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG)
71
72 #define XFS_DINODE_MIN_LOG 8
73 #define XFS_DINODE_MAX_LOG 11
74 #define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG)
75 #define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG)
76
77 #define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */
78 #define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */
79 #define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */
80
81 #define XFS_MIN_AG_BLOCKS 64
82 #define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks)
83 #define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \
84 (s)->sb_agblocks + XFS_MIN_AG_BLOCKS)
85
86
87 static void sb_from_disk(struct xfs_super_block *from,
88 struct xfs_super_block *to)
89 {
90
91 to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
92 to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
93 to->sb_dblocks = be64_to_cpu(from->sb_dblocks);
94 to->sb_rblocks = be64_to_cpu(from->sb_rblocks);
95 to->sb_rextents = be64_to_cpu(from->sb_rextents);
96 to->sb_logstart = be64_to_cpu(from->sb_logstart);
97 to->sb_rootino = be64_to_cpu(from->sb_rootino);
98 to->sb_rbmino = be64_to_cpu(from->sb_rbmino);
99 to->sb_rsumino = be64_to_cpu(from->sb_rsumino);
100 to->sb_rextsize = be32_to_cpu(from->sb_rextsize);
101 to->sb_agblocks = be32_to_cpu(from->sb_agblocks);
102 to->sb_agcount = be32_to_cpu(from->sb_agcount);
103 to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks);
104 to->sb_logblocks = be32_to_cpu(from->sb_logblocks);
105 to->sb_versionnum = be16_to_cpu(from->sb_versionnum);
106 to->sb_sectsize = be16_to_cpu(from->sb_sectsize);
107 to->sb_inodesize = be16_to_cpu(from->sb_inodesize);
108 to->sb_inopblock = be16_to_cpu(from->sb_inopblock);
109 to->sb_blocklog = from->sb_blocklog;
110 to->sb_sectlog = from->sb_sectlog;
111 to->sb_inodelog = from->sb_inodelog;
112 to->sb_inopblog = from->sb_inopblog;
113 to->sb_agblklog = from->sb_agblklog;
114 to->sb_rextslog = from->sb_rextslog;
115 to->sb_inprogress = from->sb_inprogress;
116 to->sb_imax_pct = from->sb_imax_pct;
117 to->sb_icount = be64_to_cpu(from->sb_icount);
118 to->sb_ifree = be64_to_cpu(from->sb_ifree);
119 to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks);
120 to->sb_frextents = be64_to_cpu(from->sb_frextents);
121 }
122
123 static int xfs_verify_sb(struct xfs_super_block *ondisk)
124 {
125 struct xfs_super_block sb, *sbp = &sb;
126
127 /* beXX_to_cpu(), but don't convert UUID and fsname! */
128 sb_from_disk(ondisk, sbp);
129
130 /* sanity checks, we don't want to rely on magic string only */
131 if (sbp->sb_agcount <= 0 ||
132 sbp->sb_sectsize < XFS_MIN_SECTORSIZE ||
133 sbp->sb_sectsize > XFS_MAX_SECTORSIZE ||
134 sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG ||
135 sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG ||
136 sbp->sb_sectsize != (1 << sbp->sb_sectlog) ||
137 sbp->sb_blocksize < XFS_MIN_BLOCKSIZE ||
138 sbp->sb_blocksize > XFS_MAX_BLOCKSIZE ||
139 sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
140 sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
141 sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) ||
142 sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
143 sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
144 sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
145 sbp->sb_inodelog > XFS_DINODE_MAX_LOG ||
146 sbp->sb_inodesize != (1 << sbp->sb_inodelog) ||
147 (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) ||
148 (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) ||
149 (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) ||
150 (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) ||
151 sbp->sb_dblocks == 0 ||
152 sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp) ||
153 sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp))
154 return 0;
155
156 /* TODO: version 5 has also checksum CRC32, maybe we can check it too */
157
158 return 1;
159 }
160
161 static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag)
162 {
163 struct xfs_super_block *xs;
164
165 xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block);
166 if (!xs)
167 return errno ? -errno : 1;
168
169 if (!xfs_verify_sb(xs))
170 return 1;
171
172 if (*xs->sb_fname != '\0')
173 blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname,
174 sizeof(xs->sb_fname));
175 blkid_probe_set_uuid(pr, xs->sb_uuid);
176 blkid_probe_set_block_size(pr, be16_to_cpu(xs->sb_sectsize));
177 return 0;
178 }
179
180 const struct blkid_idinfo xfs_idinfo =
181 {
182 .name = "xfs",
183 .usage = BLKID_USAGE_FILESYSTEM,
184 .probefunc = probe_xfs,
185 .magics =
186 {
187 { .magic = "XFSB", .len = 4 },
188 { NULL }
189 }
190 };
191
192 struct xlog_rec_header {
193 uint32_t h_magicno;
194 uint32_t h_dummy1[1];
195 uint32_t h_version;
196 uint32_t h_len;
197 uint32_t h_dummy2[71];
198 uint32_t h_fmt;
199 unsigned char h_uuid[16];
200 } __attribute__((packed));
201
202 #define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe
203
204 /*
205 * For very small filesystems, the minimum log size
206 * can be smaller, but that seems vanishingly unlikely
207 * when used with an external log (which is used for
208 * performance reasons; tiny conflicts with that goal).
209 */
210 #define XFS_MIN_LOG_BYTES (10 * 1024 * 1024)
211
212 #define XLOG_FMT_LINUX_LE 1
213 #define XLOG_FMT_LINUX_BE 2
214 #define XLOG_FMT_IRIX_BE 3
215
216 #define XLOG_VERSION_1 1
217 #define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */
218 #define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2)
219
220 static int xlog_valid_rec_header(struct xlog_rec_header *rhead)
221 {
222 uint32_t hlen;
223
224 if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
225 return 0;
226
227 if (!rhead->h_version ||
228 (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS)))
229 return 0;
230
231 /* LR body must have data or it wouldn't have been written */
232 hlen = be32_to_cpu(rhead->h_len);
233 if (hlen <= 0 || hlen > INT_MAX)
234 return 0;
235
236 if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) &&
237 rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) &&
238 rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE))
239 return 0;
240
241 return 1;
242 }
243
244 /* xlog record header will be in some sector in the first 256k */
245 static int probe_xfs_log(blkid_probe pr,
246 const struct blkid_idmag *mag __attribute__((__unused__)))
247 {
248 int i;
249 struct xlog_rec_header *rhead;
250 unsigned char *buf;
251
252 buf = blkid_probe_get_buffer(pr, 0, 256*1024);
253 if (!buf)
254 return errno ? -errno : 1;
255
256 if (memcmp(buf, "XFSB", 4) == 0)
257 return 1; /* this is regular XFS, ignore */
258
259 /* check the first 512 512-byte sectors */
260 for (i = 0; i < 512; i++) {
261 rhead = (struct xlog_rec_header *)&buf[i*512];
262
263 if (xlog_valid_rec_header(rhead)) {
264 blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID");
265
266 if (blkid_probe_set_magic(pr, i * 512,
267 sizeof(rhead->h_magicno),
268 (unsigned char *) &rhead->h_magicno))
269 return 1;
270
271 return 0;
272 }
273 }
274
275 return 1;
276 }
277
278 const struct blkid_idinfo xfs_log_idinfo =
279 {
280 .name = "xfs_external_log",
281 .usage = BLKID_USAGE_OTHER,
282 .probefunc = probe_xfs_log,
283 .magics = BLKID_NONE_MAGIC,
284 .minsz = XFS_MIN_LOG_BYTES,
285 };