]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/disk.c
xfs_scrub: check label for misleading characters
[thirdparty/xfsprogs-dev.git] / scrub / disk.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 */
6 #include "xfs.h"
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/statvfs.h>
13 #ifdef HAVE_SG_IO
14 # include <scsi/sg.h>
15 #endif
16 #ifdef HAVE_HDIO_GETGEO
17 # include <linux/hdreg.h>
18 #endif
19 #include "platform_defs.h"
20 #include "libfrog.h"
21 #include "path.h"
22 #include "xfs_scrub.h"
23 #include "common.h"
24 #include "disk.h"
25
26 #ifndef BLKROTATIONAL
27 # define BLKROTATIONAL _IO(0x12, 126)
28 #endif
29
30 /*
31 * Disk Abstraction
32 *
33 * These routines help us to discover the geometry of a block device,
34 * estimate the amount of concurrent IOs that we can send to it, and
35 * abstract the process of performing read verification of disk blocks.
36 */
37
38 /* Figure out how many disk heads are available. */
39 static unsigned int
40 __disk_heads(
41 struct disk *disk)
42 {
43 int iomin;
44 int ioopt;
45 unsigned short rot;
46 int error;
47
48 /* If it's not a block device, throw all the CPUs at it. */
49 if (!S_ISBLK(disk->d_sb.st_mode))
50 return nproc;
51
52 /* Non-rotational device? Throw all the CPUs at the problem. */
53 rot = 1;
54 error = ioctl(disk->d_fd, BLKROTATIONAL, &rot);
55 if (error == 0 && rot == 0)
56 return nproc;
57
58 /*
59 * Sometimes we can infer the number of devices from the
60 * min/optimal IO sizes.
61 */
62 iomin = ioopt = 0;
63 if (ioctl(disk->d_fd, BLKIOMIN, &iomin) == 0 &&
64 ioctl(disk->d_fd, BLKIOOPT, &ioopt) == 0 &&
65 iomin > 0 && ioopt > 0) {
66 return min(nproc, max(1, ioopt / iomin));
67 }
68
69 /* Rotating device? I guess? */
70 return 2;
71 }
72
73 /* Figure out how many disk heads are available. */
74 unsigned int
75 disk_heads(
76 struct disk *disk)
77 {
78 if (force_nr_threads)
79 return force_nr_threads;
80 return __disk_heads(disk);
81 }
82
83 /*
84 * Execute a SCSI VERIFY(16) to verify disk contents.
85 * For devices that support this command, this can sharply reduce the
86 * runtime of the data block verification phase if the storage device's
87 * internal bandwidth exceeds its link bandwidth. However, it only
88 * works if we're talking to a raw SCSI device, and only if we trust the
89 * firmware.
90 */
91 #ifdef HAVE_SG_IO
92 # define SENSE_BUF_LEN 64
93 # define VERIFY16_CMDLEN 16
94 # define VERIFY16_CMD 0x8F
95
96 # ifndef SG_FLAG_Q_AT_TAIL
97 # define SG_FLAG_Q_AT_TAIL 0x10
98 # endif
99 static int
100 disk_scsi_verify(
101 struct disk *disk,
102 uint64_t startblock, /* lba */
103 uint64_t blockcount) /* lba */
104 {
105 struct sg_io_hdr iohdr;
106 unsigned char cdb[VERIFY16_CMDLEN];
107 unsigned char sense[SENSE_BUF_LEN];
108 uint64_t llba;
109 uint64_t veri_len = blockcount;
110 int error;
111
112 assert(!debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"));
113
114 llba = startblock + (disk->d_start >> BBSHIFT);
115
116 /* Borrowed from sg_verify */
117 cdb[0] = VERIFY16_CMD;
118 cdb[1] = 0; /* skip PI, DPO, and byte check. */
119 cdb[2] = (llba >> 56) & 0xff;
120 cdb[3] = (llba >> 48) & 0xff;
121 cdb[4] = (llba >> 40) & 0xff;
122 cdb[5] = (llba >> 32) & 0xff;
123 cdb[6] = (llba >> 24) & 0xff;
124 cdb[7] = (llba >> 16) & 0xff;
125 cdb[8] = (llba >> 8) & 0xff;
126 cdb[9] = llba & 0xff;
127 cdb[10] = (veri_len >> 24) & 0xff;
128 cdb[11] = (veri_len >> 16) & 0xff;
129 cdb[12] = (veri_len >> 8) & 0xff;
130 cdb[13] = veri_len & 0xff;
131 cdb[14] = 0;
132 cdb[15] = 0;
133 memset(sense, 0, SENSE_BUF_LEN);
134
135 /* v3 SG_IO */
136 memset(&iohdr, 0, sizeof(iohdr));
137 iohdr.interface_id = 'S';
138 iohdr.dxfer_direction = SG_DXFER_NONE;
139 iohdr.cmdp = cdb;
140 iohdr.cmd_len = VERIFY16_CMDLEN;
141 iohdr.sbp = sense;
142 iohdr.mx_sb_len = SENSE_BUF_LEN;
143 iohdr.flags |= SG_FLAG_Q_AT_TAIL;
144 iohdr.timeout = 30000; /* 30s */
145
146 error = ioctl(disk->d_fd, SG_IO, &iohdr);
147 if (error)
148 return error;
149
150 dbg_printf("VERIFY(16) fd %d lba %"PRIu64" len %"PRIu64" info %x "
151 "status %d masked %d msg %d host %d driver %d "
152 "duration %d resid %d\n",
153 disk->d_fd, startblock, blockcount, iohdr.info,
154 iohdr.status, iohdr.masked_status, iohdr.msg_status,
155 iohdr.host_status, iohdr.driver_status, iohdr.duration,
156 iohdr.resid);
157
158 if (iohdr.info & SG_INFO_CHECK) {
159 dbg_printf("status: msg %x host %x driver %x\n",
160 iohdr.msg_status, iohdr.host_status,
161 iohdr.driver_status);
162 errno = EIO;
163 return -1;
164 }
165
166 return error;
167 }
168 #else
169 # define disk_scsi_verify(...) (ENOTTY)
170 #endif /* HAVE_SG_IO */
171
172 /* Test the availability of the kernel scrub ioctl. */
173 static bool
174 disk_can_scsi_verify(
175 struct disk *disk)
176 {
177 int error;
178
179 if (debug_tweak_on("XFS_SCRUB_NO_SCSI_VERIFY"))
180 return false;
181
182 error = disk_scsi_verify(disk, 0, 1);
183 return error == 0;
184 }
185
186 /* Open a disk device and discover its geometry. */
187 struct disk *
188 disk_open(
189 const char *pathname)
190 {
191 #ifdef HAVE_HDIO_GETGEO
192 struct hd_geometry bdgeo;
193 #endif
194 struct disk *disk;
195 bool suspicious_disk = false;
196 int lba_sz;
197 int error;
198
199 disk = calloc(1, sizeof(struct disk));
200 if (!disk)
201 return NULL;
202
203 disk->d_fd = open(pathname, O_RDONLY | O_DIRECT | O_NOATIME);
204 if (disk->d_fd < 0)
205 goto out_free;
206
207 /* Try to get LBA size. */
208 error = ioctl(disk->d_fd, BLKSSZGET, &lba_sz);
209 if (error)
210 lba_sz = 512;
211 disk->d_lbalog = log2_roundup(lba_sz);
212
213 /* Obtain disk's stat info. */
214 error = fstat(disk->d_fd, &disk->d_sb);
215 if (error)
216 goto out_close;
217
218 /* Determine bdev size, block size, and offset. */
219 if (S_ISBLK(disk->d_sb.st_mode)) {
220 error = ioctl(disk->d_fd, BLKGETSIZE64, &disk->d_size);
221 if (error)
222 disk->d_size = 0;
223 error = ioctl(disk->d_fd, BLKBSZGET, &disk->d_blksize);
224 if (error)
225 disk->d_blksize = 0;
226 #ifdef HAVE_HDIO_GETGEO
227 error = ioctl(disk->d_fd, HDIO_GETGEO, &bdgeo);
228 if (!error) {
229 /*
230 * dm devices will pass through ioctls, which means
231 * we can't use SCSI VERIFY unless the start is 0.
232 * Most dm devices don't set geometry (unlike scsi
233 * and nvme) so use a zeroed out CHS to screen them
234 * out.
235 */
236 if (bdgeo.start != 0 &&
237 (unsigned long long)bdgeo.heads * bdgeo.sectors *
238 bdgeo.sectors == 0)
239 suspicious_disk = true;
240 disk->d_start = bdgeo.start << BBSHIFT;
241 } else
242 #endif
243 disk->d_start = 0;
244 } else {
245 disk->d_size = disk->d_sb.st_size;
246 disk->d_blksize = disk->d_sb.st_blksize;
247 disk->d_start = 0;
248 }
249
250 /* Can we issue SCSI VERIFY? */
251 if (!suspicious_disk && disk_can_scsi_verify(disk))
252 disk->d_flags |= DISK_FLAG_SCSI_VERIFY;
253
254 return disk;
255 out_close:
256 close(disk->d_fd);
257 out_free:
258 free(disk);
259 return NULL;
260 }
261
262 /* Close a disk device. */
263 int
264 disk_close(
265 struct disk *disk)
266 {
267 int error = 0;
268
269 if (disk->d_fd >= 0)
270 error = close(disk->d_fd);
271 disk->d_fd = -1;
272 free(disk);
273 return error;
274 }
275
276 #define BTOLBAT(d, bytes) ((uint64_t)(bytes) >> (d)->d_lbalog)
277 #define LBASIZE(d) (1ULL << (d)->d_lbalog)
278 #define BTOLBA(d, bytes) (((uint64_t)(bytes) + LBASIZE(d) - 1) >> (d)->d_lbalog)
279
280 /* Read-verify an extent of a disk device. */
281 ssize_t
282 disk_read_verify(
283 struct disk *disk,
284 void *buf,
285 uint64_t start,
286 uint64_t length)
287 {
288 /* Convert to logical block size. */
289 if (disk->d_flags & DISK_FLAG_SCSI_VERIFY)
290 return disk_scsi_verify(disk, BTOLBAT(disk, start),
291 BTOLBA(disk, length));
292
293 return pread(disk->d_fd, buf, length, start);
294 }