blob: a6c04a2f09359f52214c42113571288ce5074570 [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001/*
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
23struct 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
87static 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
123static 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 != (1 << 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
161static 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 (strlen(xs->sb_fname))
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 return 0;
177}
178
179const struct blkid_idinfo xfs_idinfo =
180{
181 .name = "xfs",
182 .usage = BLKID_USAGE_FILESYSTEM,
183 .probefunc = probe_xfs,
184 .magics =
185 {
186 { .magic = "XFSB", .len = 4 },
187 { NULL }
188 }
189};
190
191struct xlog_rec_header {
192 uint32_t h_magicno;
193 uint32_t h_dummy1[1];
194 uint32_t h_version;
195 uint32_t h_len;
196 uint32_t h_dummy2[71];
197 uint32_t h_fmt;
198 unsigned char h_uuid[16];
199} __attribute__((packed));
200
201#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe
202
203/*
204 * For very small filesystems, the minimum log size
205 * can be smaller, but that seems vanishingly unlikely
206 * when used with an external log (which is used for
207 * performance reasons; tiny conflicts with that goal).
208 */
209#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024)
210
211#define XLOG_FMT_LINUX_LE 1
212#define XLOG_FMT_LINUX_BE 2
213#define XLOG_FMT_IRIX_BE 3
214
215#define XLOG_VERSION_1 1
216#define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */
217#define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2)
218
219static int xlog_valid_rec_header(struct xlog_rec_header *rhead)
220{
221 uint32_t hlen;
222
223 if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
224 return 0;
225
226 if (!rhead->h_version ||
227 (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS)))
228 return 0;
229
230 /* LR body must have data or it wouldn't have been written */
231 hlen = be32_to_cpu(rhead->h_len);
232 if (hlen <= 0 || hlen > INT_MAX)
233 return 0;
234
235 if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) &&
236 rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) &&
237 rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE))
238 return 0;
239
240 return 1;
241}
242
243/* xlog record header will be in some sector in the first 256k */
244static int probe_xfs_log(blkid_probe pr, const struct blkid_idmag *mag)
245{
246 int i;
247 struct xlog_rec_header *rhead;
248 unsigned char *buf;
249
250 buf = blkid_probe_get_buffer(pr, 0, 256*1024);
251 if (!buf)
252 return errno ? -errno : 1;
253
254 if (memcmp(buf, "XFSB", 4) == 0)
255 return 1; /* this is regular XFS, ignore */
256
257 /* check the first 512 512-byte sectors */
258 for (i = 0; i < 512; i++) {
259 rhead = (struct xlog_rec_header *)&buf[i*512];
260
261 if (xlog_valid_rec_header(rhead)) {
262 blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID");
263 return 0;
264 }
265 }
266
267 return 1;
268}
269
270const struct blkid_idinfo xfs_log_idinfo =
271{
272 .name = "xfs_external_log",
273 .usage = BLKID_USAGE_OTHER,
274 .probefunc = probe_xfs_log,
275 .magics = BLKID_NONE_MAGIC,
276 .minsz = XFS_MIN_LOG_BYTES,
277};