/src/util-linux/libblkid/src/superblocks/xfs.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include "crc32c.h" |
23 | | |
24 | | struct xfs_super_block { |
25 | | uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ |
26 | | uint32_t sb_blocksize; /* logical block size, bytes */ |
27 | | uint64_t sb_dblocks; /* number of data blocks */ |
28 | | uint64_t sb_rblocks; /* number of realtime blocks */ |
29 | | uint64_t sb_rextents; /* number of realtime extents */ |
30 | | unsigned char sb_uuid[16]; /* file system unique id */ |
31 | | uint64_t sb_logstart; /* starting block of log if internal */ |
32 | | uint64_t sb_rootino; /* root inode number */ |
33 | | uint64_t sb_rbmino; /* bitmap inode for realtime extents */ |
34 | | uint64_t sb_rsumino; /* summary inode for rt bitmap */ |
35 | | uint32_t sb_rextsize; /* realtime extent size, blocks */ |
36 | | uint32_t sb_agblocks; /* size of an allocation group */ |
37 | | uint32_t sb_agcount; /* number of allocation groups */ |
38 | | uint32_t sb_rbmblocks; /* number of rt bitmap blocks */ |
39 | | uint32_t sb_logblocks; /* number of log blocks */ |
40 | | |
41 | | uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ |
42 | | uint16_t sb_sectsize; /* volume sector size, bytes */ |
43 | | uint16_t sb_inodesize; /* inode size, bytes */ |
44 | | uint16_t sb_inopblock; /* inodes per block */ |
45 | | char sb_fname[12]; /* file system name */ |
46 | | uint8_t sb_blocklog; /* log2 of sb_blocksize */ |
47 | | uint8_t sb_sectlog; /* log2 of sb_sectsize */ |
48 | | uint8_t sb_inodelog; /* log2 of sb_inodesize */ |
49 | | uint8_t sb_inopblog; /* log2 of sb_inopblock */ |
50 | | uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ |
51 | | uint8_t sb_rextslog; /* log2 of sb_rextents */ |
52 | | uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ |
53 | | uint8_t sb_imax_pct; /* max % of fs for inode space */ |
54 | | /* statistics */ |
55 | | uint64_t sb_icount; /* allocated inodes */ |
56 | | uint64_t sb_ifree; /* free inodes */ |
57 | | uint64_t sb_fdblocks; /* free data blocks */ |
58 | | uint64_t sb_frextents; /* free realtime extents */ |
59 | | uint64_t sb_uquotino; /* inode for user quotas */ |
60 | | uint64_t sb_gquotino; /* inode for group or project quotas */ |
61 | | uint16_t sb_qflags; /* quota flags */ |
62 | | uint8_t sb_flags; /* misc flags */ |
63 | | uint8_t sb_shared_vn; /* reserved, zeroed */ |
64 | | uint32_t sb_inoalignmt; /* inode alignment */ |
65 | | uint32_t sb_unit; /* stripe or raid unit */ |
66 | | uint32_t sb_width; /* stripe or raid width */ |
67 | | uint8_t sb_dirblklog; /* directory block allocation granularity */ |
68 | | uint8_t sb_logsectlog; /* log sector sector size */ |
69 | | uint16_t sb_logsectsize; /* log sector size */ |
70 | | uint32_t sb_logsunit; /* log device stripe or raid unit */ |
71 | | uint32_t sb_features2; /* additional version flags */ |
72 | | uint32_t sb_bad_features2; /* mirror of sb_features2 */ |
73 | | |
74 | | /* version 5 fields */ |
75 | | uint32_t sb_features_compat; /* rw compatible flags */ |
76 | | uint32_t sb_features_ro_compat; /* ro compatible flags */ |
77 | | uint32_t sb_features_incompat; /* rw incompatible flags */ |
78 | | uint32_t sb_features_log_incompat; /* rw log incompatible flags */ |
79 | | uint32_t sb_crc; /* superblock checksum */ |
80 | | uint32_t sb_spino_align; /* sparse inode alignment */ |
81 | | uint64_t sb_pquotino; /* project quote inode */ |
82 | | uint64_t sb_lsn; /* superblock update sequence number */ |
83 | | unsigned char sb_meta_uuid[16]; /* superblock meta uuid */ |
84 | | uint64_t sb_rrmapino; /* realtime reversemapping inode */ |
85 | | } __attribute__((packed)); |
86 | | |
87 | 1.42k | #define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */ |
88 | 1.41k | #define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */ |
89 | 1.01k | #define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG) |
90 | 1.01k | #define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG) |
91 | 1.61k | #define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */ |
92 | 1.57k | #define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */ |
93 | 1.10k | #define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG) |
94 | 1.08k | #define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG) |
95 | | |
96 | 1.29k | #define XFS_DINODE_MIN_LOG 8 |
97 | 1.28k | #define XFS_DINODE_MAX_LOG 11 |
98 | 925 | #define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG) |
99 | 922 | #define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG) |
100 | | |
101 | 345 | #define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */ |
102 | | #define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */ |
103 | 341 | #define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */ |
104 | | |
105 | 207 | #define XFS_MIN_AG_BLOCKS 64 |
106 | 885 | #define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks) |
107 | 207 | #define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \ |
108 | 207 | (s)->sb_agblocks + XFS_MIN_AG_BLOCKS) |
109 | | |
110 | 9 | #define XFS_SB_VERSION_MOREBITSBIT 0x8000 |
111 | 6 | #define XFS_SB_VERSION2_CRCBIT 0x00000100 |
112 | | |
113 | | |
114 | | static void sb_from_disk(const struct xfs_super_block *from, |
115 | | struct xfs_super_block *to) |
116 | 553 | { |
117 | | |
118 | 553 | to->sb_magicnum = be32_to_cpu(from->sb_magicnum); |
119 | 553 | to->sb_blocksize = be32_to_cpu(from->sb_blocksize); |
120 | 553 | to->sb_dblocks = be64_to_cpu(from->sb_dblocks); |
121 | 553 | to->sb_rblocks = be64_to_cpu(from->sb_rblocks); |
122 | 553 | to->sb_rextents = be64_to_cpu(from->sb_rextents); |
123 | 553 | to->sb_logstart = be64_to_cpu(from->sb_logstart); |
124 | 553 | to->sb_rootino = be64_to_cpu(from->sb_rootino); |
125 | 553 | to->sb_rbmino = be64_to_cpu(from->sb_rbmino); |
126 | 553 | to->sb_rsumino = be64_to_cpu(from->sb_rsumino); |
127 | 553 | to->sb_rextsize = be32_to_cpu(from->sb_rextsize); |
128 | 553 | to->sb_agblocks = be32_to_cpu(from->sb_agblocks); |
129 | 553 | to->sb_agcount = be32_to_cpu(from->sb_agcount); |
130 | 553 | to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks); |
131 | 553 | to->sb_logblocks = be32_to_cpu(from->sb_logblocks); |
132 | 553 | to->sb_versionnum = be16_to_cpu(from->sb_versionnum); |
133 | 553 | to->sb_sectsize = be16_to_cpu(from->sb_sectsize); |
134 | 553 | to->sb_inodesize = be16_to_cpu(from->sb_inodesize); |
135 | 553 | to->sb_inopblock = be16_to_cpu(from->sb_inopblock); |
136 | 553 | to->sb_blocklog = from->sb_blocklog; |
137 | 553 | to->sb_sectlog = from->sb_sectlog; |
138 | 553 | to->sb_inodelog = from->sb_inodelog; |
139 | 553 | to->sb_inopblog = from->sb_inopblog; |
140 | 553 | to->sb_agblklog = from->sb_agblklog; |
141 | 553 | to->sb_rextslog = from->sb_rextslog; |
142 | 553 | to->sb_inprogress = from->sb_inprogress; |
143 | 553 | to->sb_imax_pct = from->sb_imax_pct; |
144 | 553 | to->sb_icount = be64_to_cpu(from->sb_icount); |
145 | 553 | to->sb_ifree = be64_to_cpu(from->sb_ifree); |
146 | 553 | to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks); |
147 | 553 | to->sb_frextents = be64_to_cpu(from->sb_frextents); |
148 | 553 | to->sb_uquotino = be64_to_cpu(from->sb_uquotino); |
149 | 553 | to->sb_gquotino = be64_to_cpu(from->sb_gquotino); |
150 | 553 | to->sb_qflags = be16_to_cpu(from->sb_qflags); |
151 | 553 | to->sb_flags = from-> sb_flags; |
152 | 553 | to->sb_shared_vn = from-> sb_shared_vn; |
153 | 553 | to->sb_inoalignmt = be32_to_cpu(from->sb_inoalignmt); |
154 | 553 | to->sb_unit = be32_to_cpu(from->sb_unit); |
155 | 553 | to->sb_width = be32_to_cpu(from->sb_width); |
156 | 553 | to->sb_dirblklog = from-> sb_dirblklog; |
157 | 553 | to->sb_logsectlog = from-> sb_logsectlog; |
158 | 553 | to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize); |
159 | 553 | to->sb_logsunit = be32_to_cpu(from->sb_logsunit); |
160 | 553 | to->sb_features2 = be32_to_cpu(from->sb_features2); |
161 | 553 | to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2); |
162 | 553 | to->sb_features_compat = be32_to_cpu(from->sb_features_compat); |
163 | 553 | to->sb_features_ro_compat = be32_to_cpu(from->sb_features_ro_compat); |
164 | 553 | to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat); |
165 | 553 | to->sb_features_log_incompat = be32_to_cpu(from->sb_features_log_incompat); |
166 | 553 | to->sb_crc = be32_to_cpu(from->sb_crc); |
167 | 553 | to->sb_spino_align = be32_to_cpu(from->sb_spino_align); |
168 | 553 | to->sb_pquotino = be64_to_cpu(from->sb_pquotino); |
169 | 553 | to->sb_lsn = be64_to_cpu(from->sb_lsn); |
170 | 553 | to->sb_rrmapino = be64_to_cpu(from->sb_rrmapino); |
171 | 553 | } |
172 | | |
173 | | static int xfs_verify_sb(const struct xfs_super_block *ondisk, blkid_probe pr, |
174 | | const struct blkid_idmag *mag) |
175 | 553 | { |
176 | 553 | struct xfs_super_block sb, *sbp = &sb; |
177 | | |
178 | | /* beXX_to_cpu(), but don't convert UUID and fsname! */ |
179 | 553 | sb_from_disk(ondisk, sbp); |
180 | | |
181 | | /* sanity checks, we don't want to rely on magic string only */ |
182 | 553 | if (sbp->sb_agcount <= 0 || |
183 | 553 | sbp->sb_sectsize < XFS_MIN_SECTORSIZE || |
184 | 553 | sbp->sb_sectsize > XFS_MAX_SECTORSIZE || |
185 | 553 | sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG || |
186 | 553 | sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG || |
187 | 553 | sbp->sb_sectsize != (1 << sbp->sb_sectlog) || |
188 | 553 | sbp->sb_blocksize < XFS_MIN_BLOCKSIZE || |
189 | 553 | sbp->sb_blocksize > XFS_MAX_BLOCKSIZE || |
190 | 553 | sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG || |
191 | 553 | sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG || |
192 | 553 | sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) || |
193 | 553 | sbp->sb_inodesize < XFS_DINODE_MIN_SIZE || |
194 | 553 | sbp->sb_inodesize > XFS_DINODE_MAX_SIZE || |
195 | 553 | sbp->sb_inodelog < XFS_DINODE_MIN_LOG || |
196 | 553 | sbp->sb_inodelog > XFS_DINODE_MAX_LOG || |
197 | 553 | sbp->sb_inodesize != (1 << sbp->sb_inodelog) || |
198 | 553 | (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) || |
199 | 553 | (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) || |
200 | 553 | (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) || |
201 | 553 | (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) || |
202 | 553 | sbp->sb_dblocks == 0 || |
203 | 553 | sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp) || |
204 | 553 | sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp)) |
205 | 493 | return 0; |
206 | | |
207 | 60 | if ((sbp->sb_versionnum & 0x0f) == 5) { |
208 | 9 | uint32_t expected, crc; |
209 | 9 | const unsigned char *csummed; |
210 | | |
211 | 9 | if (!(sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT)) |
212 | 3 | return 0; |
213 | 6 | if (!(sbp->sb_features2 & XFS_SB_VERSION2_CRCBIT)) |
214 | 3 | return 0; |
215 | | |
216 | 3 | expected = sbp->sb_crc; |
217 | 3 | csummed = blkid_probe_get_sb_buffer(pr, mag, sbp->sb_sectsize); |
218 | 3 | if (!csummed) |
219 | 0 | return 0; |
220 | | |
221 | 3 | crc = ul_crc32c_exclude_offset(~0LL, csummed, sbp->sb_sectsize, |
222 | 3 | offsetof(struct xfs_super_block, sb_crc), |
223 | 3 | sizeof_member(struct xfs_super_block, sb_crc)); |
224 | 3 | crc = bswap_32(crc ^ ~0LL); |
225 | | |
226 | 3 | if (!blkid_probe_verify_csum(pr, crc, expected)) |
227 | 3 | return 0; |
228 | 3 | } |
229 | | |
230 | 51 | return 1; |
231 | 60 | } |
232 | | |
233 | | static uint64_t xfs_fssize(const struct xfs_super_block *xs) |
234 | 51 | { |
235 | 51 | uint32_t lsize = xs->sb_logstart ? xs->sb_logblocks : 0; |
236 | 51 | uint64_t avail_blocks = be64_to_cpu(xs->sb_dblocks) - be32_to_cpu(lsize); |
237 | 51 | uint64_t fssize = avail_blocks*be32_to_cpu(xs->sb_blocksize); |
238 | | |
239 | 51 | return fssize; |
240 | 51 | } |
241 | | |
242 | | static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag) |
243 | 553 | { |
244 | 553 | const struct xfs_super_block *xs; |
245 | | |
246 | 553 | xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block); |
247 | 553 | if (!xs) |
248 | 0 | return errno ? -errno : 1; |
249 | | |
250 | 553 | if (!xfs_verify_sb(xs, pr, mag)) |
251 | 502 | return 1; |
252 | | |
253 | 51 | if (*xs->sb_fname != '\0') |
254 | 36 | blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname, |
255 | 36 | sizeof(xs->sb_fname)); |
256 | 51 | blkid_probe_set_uuid(pr, xs->sb_uuid); |
257 | 51 | blkid_probe_set_fssize(pr, xfs_fssize(xs)); |
258 | 51 | blkid_probe_set_fslastblock(pr, be64_to_cpu(xs->sb_dblocks)); |
259 | 51 | blkid_probe_set_fsblocksize(pr, be32_to_cpu(xs->sb_blocksize)); |
260 | 51 | blkid_probe_set_block_size(pr, be16_to_cpu(xs->sb_sectsize)); |
261 | 51 | return 0; |
262 | 553 | } |
263 | | |
264 | | const struct blkid_idinfo xfs_idinfo = |
265 | | { |
266 | | .name = "xfs", |
267 | | .usage = BLKID_USAGE_FILESYSTEM, |
268 | | .probefunc = probe_xfs, |
269 | | .magics = |
270 | | { |
271 | | { .magic = "XFSB", .len = 4 }, |
272 | | { NULL } |
273 | | } |
274 | | }; |
275 | | |
276 | | struct xlog_rec_header { |
277 | | uint32_t h_magicno; |
278 | | uint32_t h_dummy1[1]; |
279 | | uint32_t h_version; |
280 | | uint32_t h_len; |
281 | | uint32_t h_dummy2[71]; |
282 | | uint32_t h_fmt; |
283 | | unsigned char h_uuid[16]; |
284 | | } __attribute__((packed)); |
285 | | |
286 | | #define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe |
287 | | |
288 | | /* |
289 | | * For very small filesystems, the minimum log size |
290 | | * can be smaller, but that seems vanishingly unlikely |
291 | | * when used with an external log (which is used for |
292 | | * performance reasons; tiny conflicts with that goal). |
293 | | */ |
294 | | #define XFS_MIN_LOG_BYTES (10 * 1024 * 1024) |
295 | | |
296 | | #define XLOG_FMT_LINUX_LE 1 |
297 | | #define XLOG_FMT_LINUX_BE 2 |
298 | | #define XLOG_FMT_IRIX_BE 3 |
299 | | |
300 | 330 | #define XLOG_VERSION_1 1 |
301 | 330 | #define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */ |
302 | 330 | #define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2) |
303 | | |
304 | | static int xlog_valid_rec_header(struct xlog_rec_header *rhead) |
305 | 2.52M | { |
306 | 2.52M | uint32_t hlen; |
307 | | |
308 | 2.52M | if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM)) |
309 | 2.52M | return 0; |
310 | | |
311 | 386 | if (!rhead->h_version || |
312 | 386 | (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS))) |
313 | 200 | return 0; |
314 | | |
315 | | /* LR body must have data or it wouldn't have been written */ |
316 | 186 | hlen = be32_to_cpu(rhead->h_len); |
317 | 186 | if (hlen <= 0 || hlen > INT_MAX) |
318 | 59 | return 0; |
319 | | |
320 | 127 | if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) && |
321 | 127 | rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) && |
322 | 127 | rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE)) |
323 | 123 | return 0; |
324 | | |
325 | 4 | return 1; |
326 | 127 | } |
327 | | |
328 | | /* xlog record header will be in some sector in the first 256k */ |
329 | | static int probe_xfs_log(blkid_probe pr, |
330 | | const struct blkid_idmag *mag __attribute__((__unused__))) |
331 | 5.99k | { |
332 | 5.99k | int i; |
333 | 5.99k | struct xlog_rec_header *rhead; |
334 | 5.99k | const unsigned char *buf; |
335 | | |
336 | 5.99k | buf = blkid_probe_get_buffer(pr, 0, 256*1024); |
337 | 5.99k | if (!buf) |
338 | 0 | return errno ? -errno : 1; |
339 | | |
340 | | /* check the first 512 512-byte sectors */ |
341 | 2.53M | for (i = 0; i < 512; i++) { |
342 | | /* this is regular XFS (maybe with some sectors shift), ignore */ |
343 | 2.52M | if (memcmp(&buf[i*512], "XFSB", 4) == 0) |
344 | 1.10k | return 1; |
345 | | |
346 | 2.52M | rhead = (struct xlog_rec_header *)&buf[i*512]; |
347 | | |
348 | 2.52M | if (xlog_valid_rec_header(rhead)) { |
349 | 4 | blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID"); |
350 | | |
351 | 4 | if (blkid_probe_set_magic(pr, i * 512, |
352 | 4 | sizeof(rhead->h_magicno), |
353 | 4 | (unsigned char *) &rhead->h_magicno)) |
354 | 0 | return 1; |
355 | | |
356 | 4 | return 0; |
357 | 4 | } |
358 | 2.52M | } |
359 | | |
360 | 4.88k | return 1; |
361 | 5.99k | } |
362 | | |
363 | | const struct blkid_idinfo xfs_log_idinfo = |
364 | | { |
365 | | .name = "xfs_external_log", |
366 | | .usage = BLKID_USAGE_OTHER, |
367 | | .probefunc = probe_xfs_log, |
368 | | .magics = BLKID_NONE_MAGIC, |
369 | | .minsz = XFS_MIN_LOG_BYTES, |
370 | | }; |