Codebase list f3 / cf24370
Imported Debian patch 5.0-1 Joao Eriberto Mota Filho 9 years ago
22 changed file(s) with 2706 addition(s) and 67 deletion(s). Raw diff Collapse all Expand all
0 cscope.out
1
2 *.swp
03 *.d
14 *.o
25 *~
00 CC ?= gcc
1 CFLAGS += -std=c99 -Wall -Wextra -pedantic -MMD
1 CFLAGS += -std=c99 -Wall -Wextra -pedantic -MMD -ggdb
22
33 TARGETS = f3write f3read
4 EXPERIMENTAL_TARGETS = f3probe f3brew f3fix
45
56 all: $(TARGETS)
7 experimental: $(EXPERIMENTAL_TARGETS)
68
79 f3write: utils.o f3write.o
810 $(CC) -o $@ $^ -lm
1012 f3read: utils.o f3read.o
1113 $(CC) -o $@ $^
1214
15 f3probe: libutils.o libdevs.o libprobe.o utils.o f3probe.o
16 $(CC) -o $@ $^ -lm -ludev
17
18 f3brew: libutils.o libdevs.o f3brew.o
19 $(CC) -o $@ $^ -lm -ludev
20
21 f3fix: utils.o f3fix.o
22 $(CC) -o $@ $^ -lparted
23
1324 -include *.d
1425
15 PHONY: clean
26 PHONY: cscope clean
27
28 cscope:
29 cscope -b *.c *.h
1630
1731 clean:
18 rm -f *.o *.d $(TARGETS)
32 rm -f *.o *.d cscope.out $(TARGETS) $(EXPERIMENTAL_TARGETS)
0 ### Compile on Linux, Apple Mac, Windows/Cygwin, and FreeBSD
0 ### Compile stable software on Linux, Apple Mac, Windows/Cygwin, and FreeBSD
11
22 make
33
4 ### Use example
54
6 ./f3write /media/5EBD-5C80/
7 ./f3read /media/5EBD-5C80/
5 ### Compile experimental software on Linux
86
9 Please replace "/media/5EBD-5C80/" with the appropriate path.
7 make experimental
8
9 NOTES:
10 - Experimental software might compile on non-Linux platforms, but
11 there is no guarantee given that they are only tested on Linux.
12 - Please do not e-mail me saying that you want an experimental software
13 to run on your platform; I already know that.
14 - If you want experimental software to run on your platform,
15 help to port them, or find someone that can port them for you.
16 If you do port the software, please send me a patch to help others.
17 - Currently, f3probe, f3brew, and f3fix are experimental.
18 - f3probe and f3brew require version 1 of the library libudev to compile.
19 On Ubuntu, you can install this library with the following command:
20 sudo apt-get install libudev1 libudev-dev
21 - f3fix requires version 0 of the library libparted to compile.
22 On Ubuntu, you can install this library with the following command:
23 sudo apt-get install libparted0-dev
24
25
26 ### Use example of f3write/f3read
27
28 ./f3write /media/michel/5EBD-5C80/
29 ./f3read /media/michel/5EBD-5C80/
30
31 Please replace "/media/michel/5EBD-5C80/" with the appropriate path.
1032 USB devices are mounted in "/Volumes" on Macs.
1133
12 ### For more information see http://oss.digirati.com.br/f3/
34 For more information see http://oss.digirati.com.br/f3/
35
1336
1437 ### Files
1538
3154 So you can personalize F3 to your specific needs.
3255
3356 f3write.h2w - Script to create files exactly like H2testw.
34 Use example: 'f3write.h2w /media/5EBD-5C80/'
57 Use example: 'f3write.h2w /media/michel/5EBD-5C80/'
3558
3659 log-f3wr - Script that runs f3write and f3read, and records
3760 their output into a log file.
38 Use example: 'log-f3wr log-filename /media/5EBD-5C80/'
61 Use example: 'log-f3wr log-filename /media/michel/5EBD-5C80/'
3962
4063 Please notice that all scripts and use examples above assume that
4164 f3write, f3read, and the scripts are reachable from
4467 prefix the use examples above with 'PATH=$PATH:./' as shown below
4568 for the script log-f3wr:
4669
47 PATH=$PATH:./ log-f3wr log-filename /media/5EBD-5C80/
70 PATH=$PATH:./ log-f3wr log-filename /media/michel/5EBD-5C80/
4871
4972 The current path is represented by './' in the previous example.
0 Version 5.0 - Dec 24, 2014
1
2 * add f3probe (experimental).
3 * add f3fix (experimental).
4 * fix building issue on Macs.
5
06 Version 4.0 - Sep 9, 2014
17
28 * add support for FreeBSD.
0 f3 for Debian
1 -------------
2
3 There are several additional information about F3 in upstream homepage.
4
5 Please, see this URL: http://oss.digirati.com.br/f3
6
7 -- Joao Eriberto Mota Filho <eriberto@debian.org> Mon, 23 Feb 2015 11:55:18 -0300
0 f3 (5.0-1) experimental; urgency=medium
1
2 * New upstream release.
3 * debian/copyright:
4 - Fixed the GPL-3 license text.
5 - Updated the packaging copyright years.
6 * debian/patches/: reviewed the Makefile and README patches.
7 * debian/README.Debian: added to point to upstream homepage.
8
9 -- Joao Eriberto Mota Filho <eriberto@debian.org> Mon, 23 Feb 2015 11:19:19 -0300
10
011 f3 (4.0-2) unstable; urgency=medium
112
213 * Bumped Standards-Version to 3.9.6.
66 License: GPL-3
77
88 Files: debian/*
9 Copyright: 2013-2014 Joao Eriberto Mota Filho <eriberto@debian.org>
9 Copyright: 2013-2015 Joao Eriberto Mota Filho <eriberto@debian.org>
1010 License: GPL-3
1111
1212 License: GPL-3
1313 This program is free software: you can redistribute it and/or modify
1414 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License.
15 the Free Software Foundation, version 3 of the License.
1616 .
1717 This package is distributed in the hope that it will be useful,
1818 but WITHOUT ANY WARRANTY; without even the implied warranty of
00 Description: add GCC hardening.
11 Author: Joao Eriberto Mota Filho <eriberto@debian.org>
2 Last-Update: 2014-09-19
3 Index: f3-4.0/Makefile
2 Last-Update: 2015-02-23
3 Index: f3-5.0/Makefile
44 ===================================================================
5 --- f3-4.0.orig/Makefile
6 +++ f3-4.0/Makefile
7 @@ -6,10 +6,10 @@ TARGETS = f3write f3read
8 all: $(TARGETS)
5 --- f3-5.0.orig/Makefile
6 +++ f3-5.0/Makefile
7 @@ -8,10 +8,10 @@ all: $(TARGETS)
8 experimental: $(EXPERIMENTAL_TARGETS)
99
1010 f3write: utils.o f3write.o
1111 - $(CC) -o $@ $^ -lm
1515 - $(CC) -o $@ $^
1616 + $(CC) $(LDFLAGS) -o $@ $^
1717
18 -include *.d
19
18 f3probe: libutils.o libdevs.o libprobe.o utils.o f3probe.o
19 $(CC) -o $@ $^ -lm -ludev
0 Description: add a notice about some upstream files place and
1 remove instructions for compile.
0 Description: add a notice about some upstream files place.
21 Author: Joao Eriberto Mota Filho <eriberto@debian.org>
3 Last-Update: 2014-09-19
4 Index: f3-4.0/README
2 Last-Update: 2015-02-23
3 Index: f3-5.0/README
54 ===================================================================
6 --- f3-4.0.orig/README
7 +++ f3-4.0/README
8 @@ -1,7 +1,3 @@
9 -### Compile on Linux, Apple Mac, Windows/Cygwin, and FreeBSD
10 -
11 -make
12 -
13 ### Use example
14
15 ./f3write /media/5EBD-5C80/
16 @@ -38,6 +34,8 @@ log-f3wr - Script that runs f3write and
5 --- f3-5.0.orig/README
6 +++ f3-5.0/README
7 @@ -61,6 +61,8 @@ log-f3wr - Script that runs f3write and
178 their output into a log file.
18 Use example: 'log-f3wr log-filename /media/5EBD-5C80/'
9 Use example: 'log-f3wr log-filename /media/michel/5EBD-5C80/'
1910
2011 +(On Debian systems these files are in /usr/share/f3/ directory.)
2112 +
0 #include <stdio.h>
1 #include <stdbool.h>
2 #include <assert.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <err.h>
6
7 #include "libutils.h"
8 #include "libdevs.h"
9 #include "utils.h"
10
11 /* XXX Add parameters. */
12
13 /* XXX Avoid code duplication. This function is copied from f3write.c. */
14 static uint64_t fill_buffer(void *buf, size_t size, uint64_t offset)
15 {
16 uint8_t *p, *ptr_next_sector, *ptr_end;
17 uint64_t rn;
18
19 assert(size > 0);
20 assert(size % SECTOR_SIZE == 0);
21 assert(SECTOR_SIZE >= sizeof(offset) + sizeof(rn));
22 assert((SECTOR_SIZE - sizeof(offset)) % sizeof(rn) == 0);
23
24 p = buf;
25 ptr_end = p + size;
26 while (p < ptr_end) {
27 rn = offset;
28 memmove(p, &offset, sizeof(offset));
29 ptr_next_sector = p + SECTOR_SIZE;
30 p += sizeof(offset);
31 for (; p < ptr_next_sector; p += sizeof(rn)) {
32 rn = random_number(rn);
33 memmove(p, &rn, sizeof(rn));
34 }
35 assert(p == ptr_next_sector);
36 offset += SECTOR_SIZE;
37 }
38
39 return offset;
40 }
41
42 static void write_blocks(char *stamp_blk, struct device *dev,
43 uint64_t first_block, uint64_t last_block)
44 {
45 const int block_size = dev_get_block_size(dev);
46 uint64_t sector_offset = first_block << dev_get_block_order(dev);
47 uint64_t i;
48
49 for (i = first_block; i <= last_block; i++) {
50 sector_offset =
51 fill_buffer(stamp_blk, block_size, sector_offset);
52 if (dev_write_block(dev, stamp_blk, i))
53 warn("Failed writing block 0x%" PRIx64, i);
54 }
55 }
56
57 /* XXX Avoid code duplication. Some code of this function is copied from
58 * f3read.c.
59 */
60 /* XXX Group the results so it is not too verbose.
61 * For now, the less important reports are commented.
62 */
63 #define TOLERANCE 2
64 static void validate_sector(uint64_t expected_sector_offset,
65 const char *sector)
66 {
67 uint64_t sector_offset, rn;
68 const char *p, *ptr_end;
69 int error_count;
70
71 sector_offset = *((__typeof__(sector_offset) *) sector);
72 rn = sector_offset;
73 p = sector + sizeof(sector_offset);
74 ptr_end = sector + SECTOR_SIZE;
75 error_count = 0;
76 for (; error_count <= TOLERANCE && p < ptr_end; p += sizeof(rn)) {
77 rn = random_number(rn);
78 if (rn != *((__typeof__(rn) *) p))
79 error_count++;
80 }
81
82 if (sector_offset == expected_sector_offset) {
83 if (error_count == 0)
84 /*printf("GOOD sector 0x%" PRIx64 "\n",
85 expected_sector_offset)*/;
86 else if (error_count <= TOLERANCE)
87 printf("Changed sector 0x%" PRIx64 "\n",
88 expected_sector_offset);
89 else
90 printf("BAD matching sector 0x%" PRIx64 "\n",
91 expected_sector_offset);
92 } else if (error_count == 0) {
93 printf("Overwritten sector 0x%" PRIx64
94 ", found 0x%" PRIx64 "\n",
95 expected_sector_offset, sector_offset);
96 } else if (error_count <= TOLERANCE) {
97 printf("Overwritten and changed sector 0x%" PRIx64
98 ", found 0x%" PRIx64 "\n",
99 expected_sector_offset, sector_offset);
100 } else {
101 /*printf("BAD sector 0x%" PRIx64 "\n", expected_sector_offset)*/;
102 }
103 }
104
105 static void validate_block(uint64_t expected_sector_offset,
106 const char *probe_blk, int block_size)
107 {
108 const char *sector = probe_blk;
109 const char *stop_sector = sector + block_size;
110
111 assert(block_size % SECTOR_SIZE == 0);
112
113 while (sector < stop_sector) {
114 validate_sector(expected_sector_offset, sector);
115 expected_sector_offset += SECTOR_SIZE;
116 sector += SECTOR_SIZE;
117 }
118 }
119
120 static void read_blocks(char *probe_blk, struct device *dev,
121 uint64_t first_block, uint64_t last_block)
122 {
123 const int block_size = dev_get_block_size(dev);
124 uint64_t expected_sector_offset =
125 first_block << dev_get_block_order(dev);
126 uint64_t i;
127
128 for (i = first_block; i <= last_block; i++) {
129 if (!dev_read_block(dev, probe_blk, i))
130 validate_block(expected_sector_offset, probe_blk,
131 block_size);
132 else
133 warn("Failed reading block 0x%" PRIx64, i);
134 expected_sector_offset += block_size;
135 }
136 }
137
138 /* XXX Properly handle return errors. */
139 static void write_and_read_blocks(struct device *dev,
140 uint64_t first_block, uint64_t last_block)
141 {
142 const int block_size = dev_get_block_size(dev);
143 char stack[511 + block_size];
144 char *blk = align_512(stack);
145
146 printf("Writing blocks from 0x%" PRIx64 " to 0x%" PRIx64 "...",
147 first_block, last_block);
148 fflush(stdout);
149 write_blocks(blk, dev, first_block, last_block);
150 printf(" Done\n\n");
151
152 assert(!dev_reset(dev));
153
154 printf("Reading those blocks...");
155 fflush(stdout);
156 read_blocks(blk, dev, first_block, last_block);
157 printf(" Done\n\n");
158 }
159
160 int main(void)
161 {
162 struct device *dev;
163 uint64_t first_block = 20;
164 uint64_t last_block = 1ULL << 18;
165
166 dev = false
167 ? create_file_device("xuxu",
168 1ULL << 21, /* Real size. */
169 1ULL << 30, /* Fake size. */
170 22, /* Wrap. */
171 9, /* Block order. */
172 false) /* Keep file? */
173 : create_block_device("/dev/sdc", 0, RT_MANUAL_USB);
174 assert(dev);
175
176 write_and_read_blocks(dev, first_block, last_block);
177
178 free_device(dev);
179 return 0;
180 }
0 #include <stdbool.h>
1 #include <argp.h>
2 #include <parted/parted.h>
3
4 #include "version.h"
5
6 /* XXX Refactor utils library since f3probe barely uses it. */
7 #include "utils.h"
8
9 /* Argp's global variables. */
10 const char *argp_program_version = "F3 Fix " F3_STR_VERSION;
11
12 /* Arguments. */
13 static char adoc[] = "<DISK_DEV>";
14
15 static char doc[] = "F3 Fix -- edit the partition table of "
16 "a fake flash drive to have a single partition that fully covers "
17 "the real capacity of the drive";
18
19 static struct argp_option options[] = {
20 {"disk-type", 'd', "TYPE", 0,
21 "Disk type of the partition table", 2},
22 {"fs-type", 'f', "TYPE", 0,
23 "Type of the file system of the partition", 0},
24 {"boot", 'b', NULL, 0,
25 "Mark the partition for boot", 0},
26 {"no-boot", 'n', NULL, 0,
27 "Do not mark the partition for boot", 0},
28 {"first-sec", 'a', "SEC-NUM", 0,
29 "Sector where the partition starts", 0},
30 {"last-sec", 'l', "SEC-NUM", 0,
31 "Sector where the partition ends", 0},
32 {"list-disk-types", 'k', NULL, 0,
33 "List all supported disk types", 3},
34 {"list-fs-types", 's', NULL, 0,
35 "List all supported types of file systems", 0},
36 { 0 }
37 };
38
39 struct args {
40 bool list_disk_types;
41 bool list_fs_types;
42
43 bool boot;
44
45 /* 29 free bytes. */
46
47 const char *dev_filename;
48 PedDiskType *disk_type;
49 PedFileSystemType *fs_type;
50 PedSector first_sec;
51 PedSector last_sec;
52 };
53
54 static long long arg_to_long_long(const struct argp_state *state,
55 const char *arg)
56 {
57 char *end;
58 long long ll = strtoll(arg, &end, 0);
59 if (!arg)
60 argp_error(state, "An integer must be provided");
61 if (!*arg || *end)
62 argp_error(state, "`%s' is not an integer", arg);
63 return ll;
64 }
65
66 static error_t parse_opt(int key, char *arg, struct argp_state *state)
67 {
68 struct args *args = state->input;
69 long long ll;
70
71 switch (key) {
72 case 'd':
73 args->disk_type = ped_disk_type_get(arg);
74 if (!args->disk_type)
75 argp_error(state,
76 "Disk type `%s' is not supported; use --list-disk-types to see the supported types");
77 break;
78
79 case 'f':
80 args->fs_type = ped_file_system_type_get(arg);
81 if (!args->fs_type)
82 argp_error(state,
83 "File system type `%s' is not supported; use --list-fs-types to see the supported types");
84 break;
85
86 case 'b':
87 args->boot = true;
88 break;
89
90 case 'n':
91 args->boot = false;
92 break;
93
94 case 'a':
95 ll = arg_to_long_long(state, arg);
96 if (ll < 0)
97 argp_error(state,
98 "First sector must be greater or equal to 0");
99 args->first_sec = ll;
100 break;
101
102 case 'l':
103 ll = arg_to_long_long(state, arg);
104 if (ll < 0)
105 argp_error(state,
106 "Last sector must be greater or equal to 0");
107 args->last_sec = ll;
108 break;
109
110 case 'k':
111 args->list_disk_types = true;
112 break;
113
114 case 's':
115 args->list_fs_types = true;
116 break;
117
118 case ARGP_KEY_INIT:
119 args->dev_filename = NULL;
120 args->last_sec = -1;
121 break;
122
123 case ARGP_KEY_ARG:
124 if (args->dev_filename)
125 argp_error(state,
126 "Wrong number of arguments; only one is allowed");
127 args->dev_filename = arg;
128 break;
129
130 case ARGP_KEY_END:
131 if (args->list_disk_types || args->list_fs_types)
132 break;
133
134 if (!args->dev_filename)
135 argp_error(state,
136 "The disk device was not specified");
137
138 if (args->last_sec < 0)
139 argp_error(state,
140 "Option --last-sec is required");
141 if (args->first_sec > args->last_sec)
142 argp_error(state,
143 "Option --fist_sec must be less or equal to option --last_sec");
144 break;
145
146 default:
147 return ARGP_ERR_UNKNOWN;
148 }
149 return 0;
150 }
151
152 static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL};
153
154 static void list_disk_types(void)
155 {
156 PedDiskType *type;
157 int i = 0;
158 printf("Disk types:\n");
159 for (type = ped_disk_type_get_next(NULL); type;
160 type = ped_disk_type_get_next(type)) {
161 printf("%s\t", type->name);
162 i++;
163 if (i == 5) {
164 printf("\n");
165 i = 0;
166 }
167 }
168 if (i > 0)
169 printf("\n");
170 printf("\n");
171 }
172
173 static void list_fs_types(void)
174 {
175 PedFileSystemType *fs_type;
176 int i = 0;
177 printf("File system types:\n");
178 for (fs_type = ped_file_system_type_get_next(NULL); fs_type;
179 fs_type = ped_file_system_type_get_next(fs_type)) {
180 printf("%s\t", fs_type->name);
181 i++;
182 if (i == 5) {
183 printf("\n");
184 i = 0;
185 }
186 }
187 if (i > 0)
188 printf("\n");
189 printf("\n");
190 }
191
192 /* 0 on failure, 1 otherwise. */
193 static int fix_disk(PedDevice *dev, PedDiskType *type,
194 PedFileSystemType *fs_type, int boot, PedSector start, PedSector end)
195 {
196 PedDisk *disk;
197 PedPartition *part;
198 PedGeometry *geom;
199 PedConstraint *constraint;
200 int ret = 0;
201
202 disk = ped_disk_new_fresh(dev, type);
203 if (!disk)
204 goto out;
205
206 part = ped_partition_new(disk, PED_PARTITION_NORMAL,
207 fs_type, start, end);
208 if (!part)
209 goto disk;
210 if (boot && !ped_partition_set_flag(part, PED_PARTITION_BOOT, 1))
211 goto part;
212
213 geom = ped_geometry_new(dev, start, end - start + 1);
214 if (!geom)
215 goto part;
216 constraint = ped_constraint_exact(geom);
217 ped_geometry_destroy(geom);
218 if (!constraint)
219 goto part;
220
221 ret = ped_disk_add_partition(disk, part, constraint);
222 ped_constraint_destroy(constraint);
223 if (!ret)
224 goto part;
225 /* ped_disk_print(disk); */
226
227 ret = ped_disk_commit(disk);
228 goto disk;
229
230 part:
231 ped_partition_destroy(part);
232 disk:
233 ped_disk_destroy(disk);
234 out:
235 return ret;
236 }
237
238 int main (int argc, char *argv[])
239 {
240 struct args args = {
241 /* Defaults. */
242 .list_disk_types = false,
243 .list_fs_types = false,
244
245 .boot = true,
246
247 .disk_type = ped_disk_type_get("msdos"),
248 .fs_type = ped_file_system_type_get("fat32"),
249 .first_sec = 2048, /* Skip first 1MB. */
250 };
251
252 PedDevice *dev;
253 int ret;
254
255 /* Read parameters. */
256 argp_parse(&argp, argc, argv, 0, NULL, &args);
257 print_header(stdout, "fix");
258
259 if (args.list_disk_types)
260 list_disk_types();
261
262 if (args.list_fs_types)
263 list_fs_types();
264
265 if (args.list_disk_types || args.list_fs_types) {
266 /* If the user has asked for the types,
267 * so she doesn't want to fix the drive yet.
268 */
269 return 0;
270 }
271
272 /* XXX If @dev is a partition, refer the user to
273 * the disk of this partition.
274 */
275 dev = ped_device_get(args.dev_filename);
276 if (!dev)
277 return 1;
278
279 ret = !fix_disk(dev, args.disk_type, args.fs_type, args.boot,
280 args.first_sec, args.last_sec);
281 printf("Drive `%s' was successfully fixed\n", args.dev_filename);
282 ped_device_destroy(dev);
283 return ret;
284 }
0 #define _POSIX_C_SOURCE 200809L
1
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <argp.h>
6 #include <stdbool.h>
7 #include <assert.h>
8 #include <inttypes.h>
9 #include <sys/time.h>
10
11 #include "version.h"
12 #include "libprobe.h"
13
14 /* XXX Refactor utils library since f3probe barely uses it. */
15 #include "utils.h"
16
17 /* Argp's global variables. */
18 const char *argp_program_version = "F3 Probe " F3_STR_VERSION;
19
20 /* Arguments. */
21 static char adoc[] = "<DISK_DEV>";
22
23 static char doc[] = "F3 Probe -- probe a block device for "
24 "counterfeit flash memory. If counterfeit, "
25 "f3probe identifies the fake type and real memory size";
26
27 static struct argp_option options[] = {
28 {"debug", 'd', NULL, OPTION_HIDDEN,
29 "Enable debugging; only needed if none --debug-* option used",
30 1},
31 {"debug-real-size", 'r', "SIZE_BYTE", OPTION_HIDDEN,
32 "Real size of the emulated drive", 0},
33 {"debug-fake-size", 'f', "SIZE_BYTE", OPTION_HIDDEN,
34 "Fake size of the emulated drive", 0},
35 {"debug-wrap", 'w', "N", OPTION_HIDDEN,
36 "Wrap parameter of the emulated drive", 0},
37 {"debug-keep-file", 'k', NULL, OPTION_HIDDEN,
38 "Don't remove file used for emulating the drive", 0},
39 {"debug-unit-test", 'u', NULL, OPTION_HIDDEN,
40 "Run a unit test; it ignores all other debug options", 0},
41 {"block-order", 'b', "ORDER", 0,
42 "Force block size of the drive to 2^ORDER Bytes", 2},
43 {"destructive", 'n', NULL, 0,
44 "Do not restore blocks of the device after probing it", 0},
45 {"min-memory", 'l', NULL, 0,
46 "Trade speed for less use of memory", 0},
47 {"reset-type", 's', "TYPE", 0,
48 "Reset method to use during the probe", 0},
49 {"time-ops", 't', NULL, 0,
50 "Time reads, writes, and resets", 0},
51 { 0 }
52 };
53
54 struct args {
55 char *filename;
56
57 /* Debugging options. */
58 bool debug;
59 bool unit_test;
60 bool keep_file;
61
62 /* Behavior options. */
63 bool save;
64 bool min_mem;
65 enum reset_type reset_type;
66 bool time_ops;
67 /* 1 free bytes. */
68
69 /* Geometry. */
70 uint64_t real_size_byte;
71 uint64_t fake_size_byte;
72 int wrap;
73 int block_order;
74 };
75
76 static long long arg_to_long_long(const struct argp_state *state,
77 const char *arg)
78 {
79 char *end;
80 long long ll = strtoll(arg, &end, 0);
81 if (end == arg)
82 argp_error(state, "An integer must be provided");
83
84 /* Deal with units. */
85 switch (*end) {
86 case 's':
87 case 'S': /* Sectors */
88 ll <<= 9;
89 end++;
90 break;
91
92 case 'k':
93 case 'K': /* KB */
94 ll <<= 10;
95 end++;
96 break;
97
98 case 'm':
99 case 'M': /* MB */
100 ll <<= 20;
101 end++;
102 break;
103
104 case 'g':
105 case 'G': /* GB */
106 ll <<= 30;
107 end++;
108 break;
109
110 case 't':
111 case 'T': /* TB */
112 ll <<= 40;
113 end++;
114 break;
115 }
116
117 if (*end)
118 argp_error(state, "`%s' is not an integer", arg);
119 return ll;
120 }
121
122 static error_t parse_opt(int key, char *arg, struct argp_state *state)
123 {
124 struct args *args = state->input;
125 long long ll;
126
127 switch (key) {
128 case 'd':
129 args->debug = true;
130 break;
131
132 case 'r':
133 ll = arg_to_long_long(state, arg);
134 if (ll < 0)
135 argp_error(state,
136 "Real size must be greater or equal to zero");
137 args->real_size_byte = ll;
138 args->debug = true;
139 break;
140
141 case 'f':
142 ll = arg_to_long_long(state, arg);
143 if (ll < 0)
144 argp_error(state,
145 "Fake size must be greater or equal to zero");
146 args->fake_size_byte = ll;
147 args->debug = true;
148 break;
149
150 case 'w':
151 ll = arg_to_long_long(state, arg);
152 if (ll < 0 || ll >= 64)
153 argp_error(state,
154 "Wrap must be in the interval [0, 63]");
155 args->wrap = ll;
156 args->debug = true;
157 break;
158
159 case 'b':
160 ll = arg_to_long_long(state, arg);
161 if (ll != 0 && (ll < 9 || ll > 20))
162 argp_error(state,
163 "Block order must be in the interval [9, 20] or be zero");
164 args->block_order = ll;
165 break;
166
167 case 'k':
168 args->keep_file = true;
169 break;
170
171 case 'u':
172 args->unit_test = true;
173 break;
174
175 case 'n':
176 args->save = false;
177 break;
178
179 case 'l':
180 args->min_mem = true;
181 break;
182
183 case 's':
184 ll = arg_to_long_long(state, arg);
185 if (ll < 0 || ll >= RT_MAX)
186 argp_error(state,
187 "Reset type must be in the interval [0, %i]",
188 RT_MAX - 1);
189 args->reset_type = ll;
190 break;
191
192 case 't':
193 args->time_ops = true;
194 break;
195
196 case ARGP_KEY_INIT:
197 args->filename = NULL;
198 break;
199
200 case ARGP_KEY_ARG:
201 if (args->filename)
202 argp_error(state,
203 "Wrong number of arguments; only one is allowed");
204 args->filename = arg;
205 break;
206
207 case ARGP_KEY_END:
208 if (!args->filename)
209 argp_error(state,
210 "The disk device was not specified");
211 if (args->debug &&
212 !dev_param_valid(args->real_size_byte,
213 args->fake_size_byte, args->wrap,
214 args->block_order))
215 argp_error(state,
216 "The debugging parameters are not valid");
217 break;
218
219 default:
220 return ARGP_ERR_UNKNOWN;
221 }
222 return 0;
223 }
224
225 static struct argp argp = {options, parse_opt, adoc, doc, NULL, NULL, NULL};
226
227 struct unit_test_item {
228 uint64_t real_size_byte;
229 uint64_t fake_size_byte;
230 int wrap;
231 int block_order;
232 };
233
234 static const struct unit_test_item ftype_to_params[] = {
235 /* Smallest good drive. */
236 {1ULL << 20, 1ULL << 20, 20, 9},
237
238 /* Good, 4KB-block, 1GB drive. */
239 {1ULL << 30, 1ULL << 30, 30, 12},
240
241 /* Bad drive. */
242 {0, 1ULL << 30, 30, 9},
243
244 /* Geometry of a real limbo drive. */
245 {1777645568ULL, 32505331712ULL, 35, 9},
246
247 /* Wraparound drive. */
248 {1ULL << 31, 1ULL << 34, 31, 9},
249
250 /* Chain drive. */
251 {1ULL << 31, 1ULL << 34, 32, 9},
252
253 /* Extreme case for memory usage (limbo drive). */
254 {1ULL << 20, 1ULL << 40, 40, 9},
255 };
256
257 #define UNIT_TEST_N_CASES \
258 ((int)(sizeof(ftype_to_params)/sizeof(struct unit_test_item)))
259
260 static int unit_test(const char *filename)
261 {
262 int i, success = 0;
263 for (i = 0; i < UNIT_TEST_N_CASES; i++) {
264 const struct unit_test_item *item = &ftype_to_params[i];
265 enum fake_type origin_type = dev_param_to_type(
266 item->real_size_byte, item->fake_size_byte,
267 item->wrap, item->block_order);
268 double f_real = item->real_size_byte;
269 double f_fake = item->fake_size_byte;
270 const char *unit_real = adjust_unit(&f_real);
271 const char *unit_fake = adjust_unit(&f_fake);
272
273 enum fake_type fake_type;
274 uint64_t real_size_byte, announced_size_byte;
275 int wrap, block_order, max_probe_blocks;
276 struct device *dev;
277
278 dev = create_file_device(filename, item->real_size_byte,
279 item->fake_size_byte, item->wrap, item->block_order,
280 false);
281 assert(dev);
282 max_probe_blocks = probe_device_max_blocks(dev);
283 assert(!probe_device(dev, &real_size_byte, &announced_size_byte,
284 &wrap, &block_order));
285 free_device(dev);
286 fake_type = dev_param_to_type(real_size_byte,
287 announced_size_byte, wrap, block_order);
288
289 /* Report */
290 printf("Test %i\t\ttype/real size/fake size/module/block size\n",
291 i + 1);
292 printf("\t\t%s/%.2f %s/%.2f %s/2^%i Byte/2^%i Byte\n",
293 fake_type_to_name(origin_type),
294 f_real, unit_real, f_fake, unit_fake, item->wrap,
295 item->block_order);
296 if (real_size_byte == item->real_size_byte &&
297 announced_size_byte == item->fake_size_byte &&
298 wrap == item->wrap &&
299 block_order == item->block_order) {
300 success++;
301 printf("\t\tPerfect!\tMax # of probed blocks: %i\n\n",
302 max_probe_blocks);
303 } else {
304 double ret_f_real = real_size_byte;
305 double ret_f_fake = announced_size_byte;
306 const char *ret_unit_real = adjust_unit(&ret_f_real);
307 const char *ret_unit_fake = adjust_unit(&ret_f_fake);
308 printf("\tError\t%s/%.2f %s/%.2f %s/2^%i Byte/2^%i Byte\n\n",
309 fake_type_to_name(fake_type),
310 ret_f_real, ret_unit_real,
311 ret_f_fake, ret_unit_fake, wrap, block_order);
312 }
313 }
314
315 printf("SUMMARY: ");
316 if (success == UNIT_TEST_N_CASES)
317 printf("Perfect!\n");
318 else
319 printf("Missed %i tests out of %i\n",
320 UNIT_TEST_N_CASES - success, UNIT_TEST_N_CASES);
321 return 0;
322 }
323
324 static void report_size(const char *prefix, uint64_t bytes, int block_order)
325 {
326 double f = bytes;
327 const char *unit = adjust_unit(&f);
328 printf("%s %.2f %s (%" PRIu64 " blocks)\n", prefix, f, unit,
329 bytes >> block_order);
330 }
331
332 static void report_order(const char *prefix, int order)
333 {
334 double f = (1ULL << order);
335 const char *unit = adjust_unit(&f);
336 printf("%s %.2f %s (2^%i Bytes)\n", prefix, f, unit, order);
337 }
338
339 static void report_sector(const char *prefix, uint64_t sector)
340 {
341 printf("%s %" PRIu64 "\n", prefix, sector);
342 }
343
344 static void report_ops(const char *op, uint64_t count, uint64_t time_us)
345 {
346 printf("Probe %s op: count=%" PRIu64
347 ", total time=%.2fs, avg op time=%.2fms\n",
348 op, count, time_us / 1e6, (time_us / count) / 1e3);
349 }
350
351 static int test_device(struct args *args)
352 {
353 struct timeval t1, t2;
354 double time_s;
355 struct device *dev, *pdev;
356 enum fake_type fake_type;
357 uint64_t real_size_byte, announced_size_byte;
358 int wrap, block_order;
359 uint64_t read_count, read_time_us;
360 uint64_t write_count, write_time_us;
361 uint64_t reset_count, reset_time_us;
362 const char *final_dev_filename;
363 uint64_t last_good_sector;
364
365 dev = args->debug
366 ? create_file_device(args->filename, args->real_size_byte,
367 args->fake_size_byte, args->wrap, args->block_order,
368 args->keep_file)
369 : create_block_device(args->filename, args->block_order,
370 args->reset_type);
371 if (!dev) {
372 fprintf(stderr, "\nApplication cannot continue, finishing...\n");
373 exit(1);
374 }
375
376 if (args->time_ops) {
377 pdev = create_perf_device(dev);
378 assert(pdev);
379 dev = pdev;
380 } else {
381 pdev = NULL;
382 }
383
384 if (args->save) {
385 struct device *sdev = create_safe_device(dev,
386 probe_device_max_blocks(dev), args->min_mem);
387 if (!sdev) {
388 if (!args->min_mem)
389 fprintf(stderr, "Out of memory, try `f3probe --min-memory %s'\n",
390 dev_get_filename(dev));
391 else
392 fprintf(stderr, "Out of memory, try `f3probe --destructive %s'\nPlease back your data up before using option --destructive.\nAlternatively, you could use a machine with more memory to run f3probe.\n",
393 dev_get_filename(dev));
394 exit(1);
395 }
396 dev = sdev;
397 }
398
399 assert(!gettimeofday(&t1, NULL));
400 /* XXX Have a better error handling to recover
401 * the state of the drive.
402 */
403 assert(!probe_device(dev, &real_size_byte, &announced_size_byte,
404 &wrap, &block_order));
405 assert(!gettimeofday(&t2, NULL));
406
407 if (!args->debug && args->reset_type == RT_MANUAL_USB) {
408 printf("CAUTION\t\tCAUTION\t\tCAUTION\n");
409 printf("No more resets are needed, so do not unplug the drive\n");
410 fflush(stdout);
411 }
412
413 /* Keep free_device() as close of probe_device() as possible to
414 * make sure that the written blocks are recovered when
415 * @args->save is true.
416 */
417 if (args->time_ops)
418 perf_device_sample(pdev,
419 &read_count, &read_time_us,
420 &write_count, &write_time_us,
421 &reset_count, &reset_time_us);
422 if (args->save) {
423 printf("Probe finished, recovering blocks...");
424 fflush(stdout);
425 }
426
427 final_dev_filename = strdup(dev_get_filename(dev));
428 free_device(dev);
429 assert(final_dev_filename);
430
431 if (args->save)
432 printf(" Done\n\n");
433
434 if (strcmp(args->filename, final_dev_filename))
435 printf("\nWARNING: device `%s' moved to `%s' due to the resets\n\n",
436 args->filename, final_dev_filename);
437
438 last_good_sector = (real_size_byte >> 9) - 1;
439 fake_type = dev_param_to_type(real_size_byte, announced_size_byte,
440 wrap, block_order);
441 switch (fake_type) {
442 case FKTY_GOOD:
443 printf("Good news: The device `%s' is the real thing\n",
444 final_dev_filename);
445 break;
446
447 case FKTY_BAD:
448 printf("Bad news: The device `%s' is damaged\n",
449 final_dev_filename);
450 break;
451
452 case FKTY_LIMBO:
453 case FKTY_WRAPAROUND:
454 case FKTY_CHAIN:
455 printf("Bad news: The device `%s' is a counterfeit of type %s\n\n"
456 "You can \"fix\" this device using the following command:\n"
457 "f3fix --last-sec=%" PRIu64 " %s\n",
458 final_dev_filename, fake_type_to_name(fake_type),
459 last_good_sector, final_dev_filename);
460 break;
461
462 default:
463 assert(0);
464 break;
465 }
466
467 time_s = (t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec)/1000000.;
468 printf("\nDevice geometry:\n");
469 report_size("\t *Real* size:", real_size_byte, block_order);
470 report_size("\t Announced size:", announced_size_byte, block_order);
471 report_order("\t Module:", wrap);
472 report_order("\t Block size:", block_order);
473 assert(block_order >= 9);
474 report_sector("\tLast good sector:", last_good_sector);
475 printf("\nProbe time: %.2f seconds\n", time_s);
476
477 if (args->time_ops) {
478 report_ops("read", read_count, read_time_us);
479 report_ops("write", write_count, write_time_us);
480 report_ops("reset", reset_count, reset_time_us);
481 }
482 return 0;
483 }
484
485 int main(int argc, char **argv)
486 {
487 struct args args = {
488 /* Defaults. */
489 .debug = false,
490 .unit_test = false,
491 .keep_file = false,
492 .save = true,
493 .min_mem = false,
494 .reset_type = RT_DEFAULT,
495 .time_ops = false,
496 .real_size_byte = 1ULL << 31,
497 .fake_size_byte = 1ULL << 34,
498 .wrap = 31,
499 .block_order = 0,
500 };
501
502 /* Read parameters. */
503 argp_parse(&argp, argc, argv, 0, NULL, &args);
504 print_header(stdout, "probe");
505
506 if (args.unit_test)
507 return unit_test(args.filename);
508 return test_device(&args);
509 }
00 .\"Text automatically generated by txt2man
1 .TH F3 "1" "September 2014" "F3 4.0" "test real flash memory capacity"
1 .TH F3 "1" "December 2014" "F3 5.0" "test real flash memory capacity"
22 .SH NAME
33 \fBf3write, f3read \fP- test real flash memory capacity
44 .SH SYNOPSIS
0 #define _GNU_SOURCE
1 #define _POSIX_C_SOURCE 200809L
2 #define _FILE_OFFSET_BITS 64
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdbool.h>
7 #include <assert.h>
8 #include <string.h>
9 #include <inttypes.h>
10 #include <errno.h>
11 #include <err.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <linux/fs.h>
19 #include <linux/usbdevice_fs.h>
20 #include <libudev.h>
21
22 #include "libutils.h"
23 #include "libdevs.h"
24
25 static const char const *ftype_to_name[FKTY_MAX] = {
26 [FKTY_GOOD] = "good",
27 [FKTY_BAD] = "bad",
28 [FKTY_LIMBO] = "limbo",
29 [FKTY_WRAPAROUND] = "wraparound",
30 [FKTY_CHAIN] = "chain",
31 };
32
33 const char *fake_type_to_name(enum fake_type fake_type)
34 {
35 assert(fake_type < FKTY_MAX);
36 return ftype_to_name[fake_type];
37 }
38
39 int dev_param_valid(uint64_t real_size_byte,
40 uint64_t announced_size_byte, int wrap, int block_order)
41 {
42 int block_size;
43
44 /* Check general ranges. */
45 if (real_size_byte > announced_size_byte || wrap < 0 || wrap >= 64 ||
46 block_order < 9 || block_order > 20)
47 return false;
48
49 /* Check alignment of the sizes. */
50 block_size = 1 << block_order;
51 if (real_size_byte % block_size || announced_size_byte % block_size)
52 return false;
53
54 /* If good, @wrap must make sense. */
55 if (real_size_byte == announced_size_byte) {
56 uint64_t two_wrap = ((uint64_t)1) << wrap;
57 return announced_size_byte <= two_wrap;
58 }
59
60 return true;
61 }
62
63 enum fake_type dev_param_to_type(uint64_t real_size_byte,
64 uint64_t announced_size_byte, int wrap, int block_order)
65 {
66 uint64_t two_wrap;
67
68 assert(dev_param_valid(real_size_byte, announced_size_byte,
69 wrap, block_order));
70
71 if (real_size_byte == announced_size_byte)
72 return FKTY_GOOD;
73
74 if (real_size_byte == 0)
75 return FKTY_BAD;
76
77 /* real_size_byte < announced_size_byte */
78
79 two_wrap = ((uint64_t)1) << wrap;
80 if (two_wrap <= real_size_byte)
81 return FKTY_WRAPAROUND;
82 if (two_wrap < announced_size_byte)
83 return FKTY_CHAIN;
84 return FKTY_LIMBO;
85 }
86
87 struct device {
88 uint64_t size_byte;
89 int block_order;
90
91 int (*read_block)(struct device *dev, char *buf, int length,
92 uint64_t offset);
93 int (*write_block)(struct device *dev, const char *buf, int length,
94 uint64_t offset);
95 int (*reset)(struct device *dev);
96 void (*free)(struct device *dev);
97 const char *(*get_filename)(struct device *dev);
98 };
99
100 uint64_t dev_get_size_byte(struct device *dev)
101 {
102 return dev->size_byte;
103 }
104
105 int dev_get_block_order(struct device *dev)
106 {
107 return dev->block_order;
108 }
109
110 int dev_get_block_size(struct device *dev)
111 {
112 return 1 << dev->block_order;
113 }
114
115 int dev_read_block(struct device *dev, char *buf, uint64_t block)
116 {
117 const int block_size = 1 << dev->block_order;
118 uint64_t offset = block << dev->block_order;
119 assert(offset + block_size <= dev->size_byte);
120 return dev->read_block(dev, buf, block_size, offset);
121 }
122
123 int dev_write_block(struct device *dev, const char *buf, uint64_t block)
124 {
125 const int block_size = 1 << dev->block_order;
126 uint64_t offset = block << dev->block_order;
127 assert(offset + block_size <= dev->size_byte);
128 return dev->write_block(dev, buf, block_size, offset);
129 }
130
131 int dev_reset(struct device *dev)
132 {
133 return dev->reset ? dev->reset(dev) : 0;
134 }
135
136 void free_device(struct device *dev)
137 {
138 if (dev->free)
139 dev->free(dev);
140 free(dev);
141 }
142
143 const char *dev_get_filename(struct device *dev)
144 {
145 return dev->get_filename(dev);
146 }
147
148 struct file_device {
149 /* This must be the first field. See dev_fdev() for details. */
150 struct device dev;
151
152 const char *filename;
153 int fd;
154 uint64_t real_size_byte;
155 uint64_t address_mask;
156 };
157
158 static inline struct file_device *dev_fdev(struct device *dev)
159 {
160 return (struct file_device *)dev;
161 }
162
163 static int fdev_read_block(struct device *dev, char *buf, int length,
164 uint64_t offset)
165 {
166 struct file_device *fdev = dev_fdev(dev);
167 off_t off_ret;
168 int done;
169
170 offset &= fdev->address_mask;
171 if (offset >= fdev->real_size_byte) {
172 memset(buf, 0, length);
173 return 0;
174 }
175
176 off_ret = lseek(fdev->fd, offset, SEEK_SET);
177 if (off_ret < 0)
178 return - errno;
179 assert((uint64_t)off_ret == offset);
180
181 done = 0;
182 do {
183 ssize_t rc = read(fdev->fd, buf + done, length - done);
184 assert(rc >= 0);
185 if (!rc) {
186 /* Tried to read beyond the end of the file. */
187 assert(!done);
188 memset(buf, 0, length);
189 done += length;
190 }
191 done += rc;
192 } while (done < length);
193
194 return 0;
195 }
196
197 static int write_all(int fd, const char *buf, int count)
198 {
199 int done = 0;
200 do {
201 ssize_t rc = write(fd, buf + done, count - done);
202 if (rc < 0) {
203 /* The write() failed. */
204 return errno;
205 }
206 done += rc;
207 } while (done < count);
208 return 0;
209 }
210
211 static int fdev_write_block(struct device *dev, const char *buf, int length,
212 uint64_t offset)
213 {
214 struct file_device *fdev = dev_fdev(dev);
215 off_t off_ret;
216
217 offset &= fdev->address_mask;
218 if (offset >= fdev->real_size_byte)
219 return 0;
220
221 off_ret = lseek(fdev->fd, offset, SEEK_SET);
222 if (off_ret < 0)
223 return - errno;
224 assert((uint64_t)off_ret == offset);
225
226 return write_all(fdev->fd, buf, length);
227 }
228
229 static void fdev_free(struct device *dev)
230 {
231 struct file_device *fdev = dev_fdev(dev);
232 free((void *)fdev->filename);
233 assert(!close(fdev->fd));
234 }
235
236 static const char *fdev_get_filename(struct device *dev)
237 {
238 return dev_fdev(dev)->filename;
239 }
240
241 struct device *create_file_device(const char *filename,
242 uint64_t real_size_byte, uint64_t fake_size_byte, int wrap,
243 int block_order, int keep_file)
244 {
245 struct file_device *fdev;
246
247 fdev = malloc(sizeof(*fdev));
248 if (!fdev)
249 goto error;
250
251 fdev->filename = strdup(filename);
252 if (!fdev->filename)
253 goto fdev;
254
255 fdev->fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
256 if (fdev->fd < 0) {
257 err(errno, "Can't create file `%s'", filename);
258 goto filename;
259 }
260 if (!keep_file) {
261 /* Unlinking the file now guarantees that it won't exist if
262 * there is a crash.
263 */
264 assert(!unlink(filename));
265 }
266
267 if (!block_order) {
268 struct stat fd_stat;
269 blksize_t block_size;
270 assert(!fstat(fdev->fd, &fd_stat));
271 block_size = fd_stat.st_blksize;
272 block_order = ilog2(block_size);
273 assert(block_size == (1 << block_order));
274 }
275
276 if (!dev_param_valid(real_size_byte, fake_size_byte, wrap, block_order))
277 goto keep_file;
278
279 fdev->real_size_byte = real_size_byte;
280 fdev->address_mask = (((uint64_t)1) << wrap) - 1;
281
282 fdev->dev.size_byte = fake_size_byte;
283 fdev->dev.block_order = block_order;
284 fdev->dev.read_block = fdev_read_block;
285 fdev->dev.write_block = fdev_write_block;
286 fdev->dev.reset = NULL;
287 fdev->dev.free = fdev_free;
288 fdev->dev.get_filename = fdev_get_filename;
289
290 return &fdev->dev;
291
292 keep_file:
293 if (keep_file)
294 unlink(filename);
295 assert(!close(fdev->fd));
296 filename:
297 free((void *)fdev->filename);
298 fdev:
299 free(fdev);
300 error:
301 return NULL;
302 }
303
304 struct block_device {
305 /* This must be the first field. See dev_bdev() for details. */
306 struct device dev;
307
308 const char *filename;
309 int fd;
310 };
311
312 static inline struct block_device *dev_bdev(struct device *dev)
313 {
314 return (struct block_device *)dev;
315 }
316
317 static int read_all(int fd, char *buf, int count)
318 {
319 int done = 0;
320 do {
321 ssize_t rc = read(fd, buf + done, count - done);
322 assert(rc >= 0); /* Did the read() went right? */
323 assert(rc != 0); /* We should never hit the end of the file. */
324 done += rc;
325 } while (done < count);
326 return 0;
327 }
328
329 static int bdev_read_block(struct device *dev, char *buf, int length,
330 uint64_t offset)
331 {
332 struct block_device *bdev = dev_bdev(dev);
333 off_t off_ret = lseek(bdev->fd, offset, SEEK_SET);
334 if (off_ret < 0)
335 return - errno;
336 assert((uint64_t)off_ret == offset);
337 return read_all(bdev->fd, buf, length);
338 }
339
340 static int bdev_write_block(struct device *dev, const char *buf, int length,
341 uint64_t offset)
342 {
343 struct block_device *bdev = dev_bdev(dev);
344 off_t off_ret = lseek(bdev->fd, offset, SEEK_SET);
345 if (off_ret < 0)
346 return - errno;
347 assert((uint64_t)off_ret == offset);
348 return write_all(bdev->fd, buf, length);
349 }
350
351 static inline int bdev_open(const char *filename)
352 {
353 return open(filename, O_RDWR | O_DIRECT | O_SYNC);
354 }
355
356 static struct udev_device *map_dev_to_usb_dev(struct udev_device *dev)
357 {
358 struct udev_device *usb_dev;
359
360 /* The device pointed to by dev contains information about
361 * the USB device.
362 * In order to get information about the USB device,
363 * get the parent device with the subsystem/devtype pair of
364 * "usb"/"usb_device".
365 * This will be several levels up the tree,
366 * but the function will find it.
367 */
368 usb_dev = udev_device_get_parent_with_subsystem_devtype(
369 dev, "usb", "usb_device");
370
371 /* @usb_dev is not referenced, and will be freed when
372 * the child (i.e. @dev) is freed.
373 * See udev_device_get_parent_with_subsystem_devtype() for
374 * details.
375 */
376 return udev_device_ref(usb_dev);
377 }
378
379 static struct udev_device *dev_from_block_fd(struct udev *udev, int block_fd)
380 {
381 struct stat fd_stat;
382
383 assert(!fstat(block_fd, &fd_stat));
384 if (!S_ISBLK(fd_stat.st_mode))
385 err(EINVAL, "FD %i is not a block device", block_fd);
386 return udev_device_new_from_devnum(udev, 'b', fd_stat.st_rdev);
387 }
388
389 static struct udev_device *map_block_to_usb_dev(struct udev *udev, int block_fd)
390 {
391 struct udev_device *dev, *usb_dev;
392
393 dev = dev_from_block_fd(udev, block_fd);
394 assert(dev);
395 usb_dev = map_dev_to_usb_dev(dev);
396 udev_device_unref(dev);
397 return usb_dev;
398 }
399
400 static struct udev_monitor *create_monitor(struct udev *udev,
401 const char *subsystem, const char *devtype)
402 {
403 struct udev_monitor *mon;
404 int mon_fd, flags;
405
406 mon = udev_monitor_new_from_netlink(udev, "udev");
407 assert(mon);
408 assert(!udev_monitor_filter_add_match_subsystem_devtype(mon,
409 subsystem, devtype));
410 assert(!udev_monitor_enable_receiving(mon));
411 mon_fd = udev_monitor_get_fd(mon);
412 assert(mon_fd >= 0);
413 flags = fcntl(mon_fd, F_GETFL);
414 assert(flags >= 0);
415 assert(!fcntl(mon_fd, F_SETFL, flags & ~O_NONBLOCK));
416
417 return mon;
418 }
419
420 static char *wait_for_add_action(struct udev *udev,
421 const char *id_vendor, const char *id_product, const char *serial)
422 {
423 char *devnode = NULL;
424 bool done = false;
425 struct udev_monitor *mon;
426
427 mon = create_monitor(udev, "block", "disk");
428 assert(mon);
429 do {
430 struct udev_device *dev, *usb_dev;
431
432 dev = udev_monitor_receive_device(mon);
433 assert(dev);
434 if (strcmp(udev_device_get_action(dev), "add")) {
435 udev_device_unref(dev);
436 continue;
437 }
438
439 usb_dev = map_dev_to_usb_dev(dev);
440 if (usb_dev &&
441 !strcmp(udev_device_get_sysattr_value(usb_dev,
442 "idVendor"), id_vendor) &&
443 !strcmp(udev_device_get_sysattr_value(usb_dev,
444 "idProduct"), id_product) &&
445 !strcmp(udev_device_get_sysattr_value(usb_dev,
446 "serial"), serial)) {
447 devnode = strdup(udev_device_get_devnode(dev));
448 assert(devnode);
449 done = true;
450 }
451
452 udev_device_unref(usb_dev);
453 udev_device_unref(dev);
454 } while (!done);
455 assert(!udev_monitor_unref(mon));
456
457 return devnode;
458 }
459
460 static int bdev_manual_usb_reset(struct device *dev)
461 {
462 struct block_device *bdev = dev_bdev(dev);
463 struct udev *udev;
464 struct udev_device *usb_dev;
465 const char *id_vendor, *id_product, *serial, *devnode;
466
467 udev = udev_new();
468 if (!udev)
469 err(errno, "Can't create udev");
470
471 /* Obtain @bus_num and @dev_num of the USB device to monitor. */
472 usb_dev = map_block_to_usb_dev(udev, bdev->fd);
473 if (!usb_dev)
474 errx(1, "Unable to find USB parent device of block device `%s'",
475 bdev->filename);
476 id_vendor = udev_device_get_sysattr_value(usb_dev, "idVendor");
477 id_product = udev_device_get_sysattr_value(usb_dev, "idProduct");
478 serial = udev_device_get_sysattr_value(usb_dev, "serial");
479
480 /* Close @bdev->fd before the drive is removed to increase
481 * the chance that the device will receive the same filename.
482 * The code is robust enough to deal with the case the drive doesn't
483 * receive the same file name, though.
484 */
485 assert(!close(bdev->fd));
486
487 printf("Please unplug and plug back the USB drive. Waiting...");
488 fflush(stdout);
489 devnode = wait_for_add_action(udev, id_vendor, id_product, serial);
490 assert(devnode);
491 printf(" Thanks\n\n");
492
493 udev_device_unref(usb_dev);
494 assert(!udev_unref(udev));
495
496 bdev->fd = bdev_open(devnode);
497 if (bdev->fd < 0)
498 err(errno, "Can't REopen device `%s'", devnode);
499 free((void *)bdev->filename);
500 bdev->filename = devnode;
501
502 return 0;
503 }
504
505 /* Return an open fd to the underlying hardware of the block device. */
506 static int usb_fd_from_block_dev(int block_fd, int open_flags)
507 {
508 struct udev *udev;
509 struct udev_device *dev;
510 const char *usb_filename;
511 int usb_fd;
512
513 udev = udev_new();
514 if (!udev)
515 err(errno, "Can't create udev");
516
517 dev = map_block_to_usb_dev(udev, block_fd);
518 assert(dev);
519
520 usb_filename = udev_device_get_devnode(dev);
521 if (!usb_filename)
522 err(EINVAL, "Block device is not backed by a USB device");
523
524 usb_fd = open(usb_filename, open_flags | O_NONBLOCK);
525 if (usb_fd < 0)
526 err(errno, "Can't open device `%s'", usb_filename);
527
528 udev_device_unref(dev);
529 assert(!udev_unref(udev));
530 return usb_fd;
531 }
532
533 static int bdev_usb_reset(struct device *dev)
534 {
535 struct block_device *bdev = dev_bdev(dev);
536 int usb_fd;
537
538 usb_fd = usb_fd_from_block_dev(bdev->fd, O_WRONLY);
539 assert(usb_fd >= 0);
540
541 assert(!close(bdev->fd));
542 assert(!ioctl(usb_fd, USBDEVFS_RESET));
543 assert(!close(usb_fd));
544 bdev->fd = bdev_open(bdev->filename);
545 if (bdev->fd < 0)
546 err(errno, "Can't REopen device `%s'", bdev->filename);
547 return 0;
548 }
549
550 static void bdev_free(struct device *dev)
551 {
552 struct block_device *bdev = dev_bdev(dev);
553 if (bdev->fd >= 0)
554 assert(!close(bdev->fd));
555 free((void *)bdev->filename);
556 }
557
558 static const char *bdev_get_filename(struct device *dev)
559 {
560 return dev_bdev(dev)->filename;
561 }
562
563 static struct udev_device *map_partition_to_disk(struct udev_device *dev)
564 {
565 struct udev_device *disk_dev;
566
567 disk_dev = udev_device_get_parent_with_subsystem_devtype(
568 dev, "block", "disk");
569
570 /* @disk_dev is not referenced, and will be freed when
571 * the child (i.e. @dev) is freed.
572 * See udev_device_get_parent_with_subsystem_devtype() for
573 * details.
574 */
575 return udev_device_ref(disk_dev);
576 }
577
578 struct device *create_block_device(const char *filename, int block_order,
579 enum reset_type rt)
580 {
581 struct block_device *bdev;
582 struct udev *udev;
583 struct udev_device *fd_dev, *usb_dev;
584 const char *s;
585
586 bdev = malloc(sizeof(*bdev));
587 if (!bdev)
588 goto error;
589
590 bdev->filename = strdup(filename);
591 if (!bdev->filename)
592 goto bdev;
593
594 bdev->fd = bdev_open(filename);
595 if (bdev->fd < 0) {
596 if (errno == EACCES && getuid()) {
597 fprintf(stderr, "Your username doesn't have access to device `%s'.\n"
598 "Try to run this program as root:\n"
599 "sudo f3probe %s\n"
600 "In case you don't have access to root, use f3write/f3read.\n",
601 filename, filename);
602 } else {
603 err(errno, "Can't open device `%s'", filename);
604 }
605 goto filename;
606 }
607
608 /* Make sure that @bdev->fd is a disk, not a partition, and that
609 * it is in fact backed by a USB device.
610 */
611 udev = udev_new();
612 if (!udev) {
613 warn("Can't create udev");
614 goto fd;
615 }
616 fd_dev = dev_from_block_fd(udev, bdev->fd);
617 if (!fd_dev) {
618 fprintf(stderr, "Can't create udev device from `%s'\n",
619 filename);
620 goto udev;
621 }
622 assert(!strcmp(udev_device_get_subsystem(fd_dev), "block"));
623 s = udev_device_get_devtype(fd_dev);
624 if (!strcmp(s, "partition")) {
625 struct udev_device *disk_dev = map_partition_to_disk(fd_dev);
626 assert(disk_dev);
627 s = udev_device_get_devnode(disk_dev);
628 fprintf(stderr, "Device `%s' is a partition of disk device `%s'.\n"
629 "You must run f3probe on the disk device as follows:\n"
630 "f3probe %s\n",
631 filename, s, s);
632 udev_device_unref(disk_dev);
633 goto fd_dev;
634 } else if (strcmp(s, "disk")) {
635 fprintf(stderr, "Device `%s' is not a disk, but `%s'",
636 filename, s);
637 goto fd_dev;
638 }
639 usb_dev = map_dev_to_usb_dev(fd_dev);
640 if (!usb_dev) {
641 fprintf(stderr, "Device `%s' is not backed by a USB device",
642 filename);
643 goto fd_dev;
644 }
645 udev_device_unref(usb_dev);
646 udev_device_unref(fd_dev);
647 assert(!udev_unref(udev));
648
649 switch (rt) {
650 case RT_MANUAL_USB:
651 bdev->dev.reset = bdev_manual_usb_reset;
652 break;
653 case RT_USB:
654 bdev->dev.reset = bdev_usb_reset;
655 break;
656 default:
657 assert(0);
658 }
659
660 assert(!ioctl(bdev->fd, BLKGETSIZE64, &bdev->dev.size_byte));
661
662 if (!block_order) {
663 int block_size;
664 assert(!ioctl(bdev->fd, BLKBSZGET, &block_size));
665 block_order = ilog2(block_size);
666 assert(block_size == (1 << block_order));
667 }
668 bdev->dev.block_order = block_order;
669
670 bdev->dev.read_block = bdev_read_block;
671 bdev->dev.write_block = bdev_write_block;
672 bdev->dev.free = bdev_free;
673 bdev->dev.get_filename = bdev_get_filename;
674
675 return &bdev->dev;
676
677 fd_dev:
678 udev_device_unref(fd_dev);
679 udev:
680 assert(!udev_unref(udev));
681 fd:
682 assert(!close(bdev->fd));
683 filename:
684 free((void *)bdev->filename);
685 bdev:
686 free(bdev);
687 error:
688 return NULL;
689 }
690
691 struct perf_device {
692 /* This must be the first field. See dev_pdev() for details. */
693 struct device dev;
694
695 struct device *shadow_dev;
696
697 uint64_t read_count;
698 uint64_t read_time_us;
699 uint64_t write_count;
700 uint64_t write_time_us;
701 uint64_t reset_count;
702 uint64_t reset_time_us;
703 };
704
705 static inline struct perf_device *dev_pdev(struct device *dev)
706 {
707 return (struct perf_device *)dev;
708 }
709
710 static inline uint64_t diff_timeval_us(const struct timeval *t1,
711 const struct timeval *t2)
712 {
713 return (t2->tv_sec - t1->tv_sec) * 1000000ULL +
714 t2->tv_usec - t1->tv_usec;
715 }
716
717 static int pdev_read_block(struct device *dev, char *buf, int length,
718 uint64_t offset)
719 {
720 struct perf_device *pdev = dev_pdev(dev);
721 struct timeval t1, t2;
722 int rc;
723
724 assert(!gettimeofday(&t1, NULL));
725 rc = pdev->shadow_dev->read_block(pdev->shadow_dev, buf,
726 length, offset);
727 assert(!gettimeofday(&t2, NULL));
728 pdev->read_count++;
729 pdev->read_time_us += diff_timeval_us(&t1, &t2);
730 return rc;
731 }
732
733 static int pdev_write_block(struct device *dev, const char *buf, int length,
734 uint64_t offset)
735 {
736 struct perf_device *pdev = dev_pdev(dev);
737 struct timeval t1, t2;
738 int rc;
739
740 assert(!gettimeofday(&t1, NULL));
741 rc = pdev->shadow_dev->write_block(pdev->shadow_dev, buf,
742 length, offset);
743 assert(!gettimeofday(&t2, NULL));
744 pdev->write_count++;
745 pdev->write_time_us += diff_timeval_us(&t1, &t2);
746 return rc;
747 }
748
749 static int pdev_reset(struct device *dev)
750 {
751 struct perf_device *pdev = dev_pdev(dev);
752 struct timeval t1, t2;
753 int rc;
754
755 assert(!gettimeofday(&t1, NULL));
756 rc = dev_reset(pdev->shadow_dev);
757 assert(!gettimeofday(&t2, NULL));
758 pdev->reset_count++;
759 pdev->reset_time_us += diff_timeval_us(&t1, &t2);
760 return rc;
761 }
762
763 static void pdev_free(struct device *dev)
764 {
765 struct perf_device *pdev = dev_pdev(dev);
766 free_device(pdev->shadow_dev);
767 }
768
769 static const char *pdev_get_filename(struct device *dev)
770 {
771 return dev_get_filename(dev_pdev(dev)->shadow_dev);
772 }
773
774 struct device *pdev_detach_and_free(struct device *dev)
775 {
776 struct perf_device *pdev = dev_pdev(dev);
777 struct device *shadow_dev = pdev->shadow_dev;
778 pdev->shadow_dev = NULL;
779 pdev->dev.free = NULL;
780 free_device(&pdev->dev);
781 return shadow_dev;
782 }
783
784 struct device *create_perf_device(struct device *dev)
785 {
786 struct perf_device *pdev;
787
788 pdev = malloc(sizeof(*pdev));
789 if (!pdev)
790 return NULL;
791
792 pdev->shadow_dev = dev;
793 pdev->read_count = 0;
794 pdev->read_time_us = 0;
795 pdev->write_count = 0;
796 pdev->write_time_us = 0;
797 pdev->reset_count = 0;
798 pdev->reset_time_us = 0;
799
800 pdev->dev.size_byte = dev->size_byte;
801 pdev->dev.block_order = dev->block_order;
802 pdev->dev.read_block = pdev_read_block;
803 pdev->dev.write_block = pdev_write_block;
804 pdev->dev.reset = pdev_reset;
805 pdev->dev.free = pdev_free;
806 pdev->dev.get_filename = pdev_get_filename;
807
808 return &pdev->dev;
809 }
810
811 void perf_device_sample(struct device *dev,
812 uint64_t *pread_count, uint64_t *pread_time_us,
813 uint64_t *pwrite_count, uint64_t *pwrite_time_us,
814 uint64_t *preset_count, uint64_t *preset_time_us)
815 {
816 struct perf_device *pdev = dev_pdev(dev);
817
818 if (pread_count)
819 *pread_count = pdev->read_count;
820 if (pread_time_us)
821 *pread_time_us = pdev->read_time_us;
822
823 if (pwrite_count)
824 *pwrite_count = pdev->write_count;
825 if (pwrite_time_us)
826 *pwrite_time_us = pdev->write_time_us;
827
828 if (preset_count)
829 *preset_count = pdev->reset_count;
830 if (preset_time_us)
831 *preset_time_us = pdev->reset_time_us;
832 }
833
834 #define SDEV_BITMAP_WORD long
835 #define SDEV_BITMAP_BITS_PER_WORD (8*sizeof(SDEV_BITMAP_WORD))
836 struct safe_device {
837 /* This must be the first field. See dev_sdev() for details. */
838 struct device dev;
839
840 struct device *shadow_dev;
841
842 char *saved_blocks;
843 uint64_t *sb_offsets;
844 SDEV_BITMAP_WORD *sb_bitmap;
845 int sb_n;
846 int sb_max;
847 };
848
849 static inline struct safe_device *dev_sdev(struct device *dev)
850 {
851 return (struct safe_device *)dev;
852 }
853
854 static int sdev_read_block(struct device *dev, char *buf, int length,
855 uint64_t offset)
856 {
857 struct safe_device *sdev = dev_sdev(dev);
858 return sdev->shadow_dev->read_block(sdev->shadow_dev, buf,
859 length, offset);
860 }
861
862 static int sdev_save_block(struct safe_device *sdev,
863 int length, uint64_t offset)
864 {
865 const int block_order = dev_get_block_order(sdev->shadow_dev);
866 lldiv_t idx = lldiv(offset >> block_order, SDEV_BITMAP_BITS_PER_WORD);
867 SDEV_BITMAP_WORD set_bit = (SDEV_BITMAP_WORD)1 << idx.rem;
868 char *block;
869 int rc;
870
871 /* The current implementation doesn't support variable lengths. */
872 assert(length == dev_get_block_size(sdev->shadow_dev));
873
874 /* Is this block already saved? */
875 if (!sdev->sb_bitmap) {
876 int i;
877 /* Running without bitmap. */
878 for (i = 0; i < sdev->sb_n; i++)
879 if (sdev->sb_offsets[i] == offset) {
880 /* The block at @offset is already saved. */
881 return 0;
882 }
883 } else if (sdev->sb_bitmap[idx.quot] & set_bit) {
884 /* The block at @offset is already saved. */
885 return 0;
886 }
887
888 /* The block at @offset hasn't been saved before. Save this block. */
889 assert(sdev->sb_n < sdev->sb_max);
890 block = (char *)align_512(sdev->saved_blocks) +
891 (sdev->sb_n << block_order);
892 rc = sdev->shadow_dev->read_block(sdev->shadow_dev, block,
893 length, offset);
894 if (rc)
895 return rc;
896
897 /* Bookkeeping. */
898 if (sdev->sb_bitmap)
899 sdev->sb_bitmap[idx.quot] |= set_bit;
900 sdev->sb_offsets[sdev->sb_n] = offset;
901 sdev->sb_n++;
902 return 0;
903 }
904
905 static int sdev_write_block(struct device *dev, const char *buf, int length,
906 uint64_t offset)
907 {
908 struct safe_device *sdev = dev_sdev(dev);
909 int rc;
910
911 rc = sdev_save_block(sdev, length, offset);
912 if (rc)
913 return rc;
914
915 return sdev->shadow_dev->write_block(sdev->shadow_dev, buf,
916 length, offset);
917 }
918
919 static int sdev_reset(struct device *dev)
920 {
921 return dev_reset(dev_sdev(dev)->shadow_dev);
922 }
923
924 static void sdev_free(struct device *dev)
925 {
926 struct safe_device *sdev = dev_sdev(dev);
927
928 if (sdev->sb_n > 0) {
929 char *first_block = align_512(sdev->saved_blocks);
930 char *block = first_block +
931 ((sdev->sb_n - 1) <<
932 dev_get_block_order(sdev->shadow_dev));
933 uint64_t *poffset = &sdev->sb_offsets[sdev->sb_n - 1];
934 int block_size = dev_get_block_size(sdev->shadow_dev);
935
936 /* Restore blocks in reverse order to cope with
937 * wraparound and chain drives.
938 */
939 do {
940 int rc = sdev->shadow_dev->write_block(
941 sdev->shadow_dev, block, block_size, *poffset);
942 if (rc) {
943 /* Do not abort, try to recover all bocks. */
944 warn("Failed to recover block at offset 0x%"
945 PRIx64 " due to a write error",
946 *poffset);
947 }
948 block -= block_size;
949 poffset--;
950 } while (block >= first_block);
951 }
952
953 free(sdev->sb_bitmap);
954 free(sdev->sb_offsets);
955 free(sdev->saved_blocks);
956 free_device(sdev->shadow_dev);
957 }
958
959 static const char *sdev_get_filename(struct device *dev)
960 {
961 return dev_get_filename(dev_sdev(dev)->shadow_dev);
962 }
963
964 struct device *create_safe_device(struct device *dev, int max_blocks,
965 int min_memory)
966 {
967 struct safe_device *sdev;
968 const int block_order = dev_get_block_order(dev);
969 uint64_t length;
970
971 sdev = malloc(sizeof(*sdev));
972 if (!sdev)
973 goto error;
974
975 length = 511 + (max_blocks << block_order);
976 sdev->saved_blocks = malloc(length);
977 if (!sdev->saved_blocks)
978 goto sdev;
979
980 sdev->sb_offsets = malloc(max_blocks * sizeof(*sdev->sb_offsets));
981 if (!sdev->sb_offsets)
982 goto saved_blocks;
983
984 if (!min_memory) {
985 lldiv_t idx = lldiv(dev_get_size_byte(dev) >> block_order,
986 SDEV_BITMAP_BITS_PER_WORD);
987 length = (idx.quot + (idx.rem ? 1 : 0)) *
988 sizeof(SDEV_BITMAP_WORD);
989 sdev->sb_bitmap = malloc(length);
990 if (!sdev->sb_bitmap)
991 goto offsets;
992 memset(sdev->sb_bitmap, 0, length);
993 } else {
994 sdev->sb_bitmap = NULL;
995 }
996
997 sdev->shadow_dev = dev;
998 sdev->sb_n = 0;
999 sdev->sb_max = max_blocks;
1000
1001 sdev->dev.size_byte = dev->size_byte;
1002 sdev->dev.block_order = block_order;
1003 sdev->dev.read_block = sdev_read_block;
1004 sdev->dev.write_block = sdev_write_block;
1005 sdev->dev.reset = sdev_reset;
1006 sdev->dev.free = sdev_free;
1007 sdev->dev.get_filename = sdev_get_filename;
1008
1009 return &sdev->dev;
1010
1011 offsets:
1012 free(sdev->sb_offsets);
1013 saved_blocks:
1014 free(sdev->saved_blocks);
1015 sdev:
1016 free(sdev);
1017 error:
1018 return NULL;
1019 }
0 #ifndef HEADER_LIBDEVS_H
1 #define HEADER_LIBDEVS_H
2
3 #include <stdint.h>
4
5 /*
6 * Device model
7 */
8
9 enum fake_type {
10 /* Device is good. */
11 FKTY_GOOD,
12
13 /* Device is at least partially damaged. */
14 FKTY_BAD,
15
16 /* Device discards data after a given limit. */
17 FKTY_LIMBO,
18
19 /* Device overwrites data after a given limit. */
20 FKTY_WRAPAROUND,
21
22 /* Device is a sequence of wraparound and limbo regions. */
23 FKTY_CHAIN,
24
25 FKTY_MAX
26 };
27
28 const char *fake_type_to_name(enum fake_type fake_type);
29
30 int dev_param_valid(uint64_t real_size_byte,
31 uint64_t announced_size_byte, int wrap, int block_order);
32
33 enum fake_type dev_param_to_type(uint64_t real_size_byte,
34 uint64_t announced_size_byte, int wrap, int block_order);
35
36 /*
37 * Abstract device
38 */
39
40 struct device;
41
42 /* Properties. */
43 uint64_t dev_get_size_byte(struct device *dev);
44 int dev_get_block_order(struct device *dev);
45 int dev_get_block_size(struct device *dev);
46
47 /* Methods. */
48 int dev_read_block(struct device *dev, char *buf, uint64_t block);
49 int dev_write_block(struct device *dev, const char *buf, uint64_t block);
50 int dev_reset(struct device *dev);
51 void free_device(struct device *dev);
52 /* File name of the device.
53 * This information is important because the filename may change due to resets.
54 */
55 const char *dev_get_filename(struct device *dev);
56
57 static inline int dev_write_and_reset(struct device *dev, const char *buf,
58 uint64_t block)
59 {
60 int rc = dev_write_block(dev, buf, block);
61 return rc ? rc : dev_reset(dev);
62 }
63
64 /*
65 * Concrete devices
66 */
67
68 struct device *create_file_device(const char *filename,
69 uint64_t real_size_byte, uint64_t fake_size_byte, int wrap,
70 int block_order, int keep_file);
71
72 /* XXX Add support for block devices backed by SCSI and ATA. */
73 enum reset_type {
74 RT_MANUAL_USB = 0,
75 RT_USB,
76 RT_MAX
77 };
78 #define RT_DEFAULT RT_MANUAL_USB
79
80 struct device *create_block_device(const char *filename, int block_order,
81 enum reset_type rt);
82
83 struct device *create_perf_device(struct device *dev);
84 void perf_device_sample(struct device *dev,
85 uint64_t *pread_count, uint64_t *pread_time_us,
86 uint64_t *pwrite_count, uint64_t *pwrite_time_us,
87 uint64_t *preset_count, uint64_t *preset_time_us);
88 /* Detach the shadow device of @pdev, free @pdev, and return
89 * the shadow device.
90 */
91 struct device *pdev_detach_and_free(struct device *dev);
92
93 struct device *create_safe_device(struct device *dev, int max_blocks,
94 int min_memory);
95
96 #endif /* HEADER_LIBDEVS_H */
0 #include <stdlib.h>
1 #include <string.h>
2 #include <stdbool.h>
3 #include <assert.h>
4 #include <math.h>
5 #include <errno.h>
6
7 #include "libutils.h"
8 #include "libprobe.h"
9
10 static inline int equal_blk(struct device *dev, const char *b1, const char *b2)
11 {
12 return !memcmp(b1, b2, dev_get_block_size(dev));
13 }
14
15 /* Return true if @b1 and b2 are at most @tolerance_byte bytes different. */
16 static int similar_blk(struct device *dev, const char *b1, const char *b2,
17 int tolerance_byte)
18 {
19 const int block_size = dev_get_block_size(dev);
20 int i;
21
22 for (i = 0; i < block_size; i++) {
23 if (*b1 != *b2) {
24 tolerance_byte--;
25 if (tolerance_byte <= 0)
26 return false;
27 }
28 b1++;
29 b2++;
30 }
31 return true;
32 }
33
34 /* Return true if the block at @pos is damaged. */
35 static int test_block(struct device *dev,
36 const char *stamp_blk, char *probe_blk, uint64_t pos)
37 {
38 /* Write block. */
39 if (dev_write_block(dev, stamp_blk, pos) &&
40 dev_write_block(dev, stamp_blk, pos))
41 return true;
42
43 /* Reset. */
44 if (dev_reset(dev) && dev_reset(dev))
45 return true;
46
47 /*
48 * Test block.
49 */
50
51 if (dev_read_block(dev, probe_blk, pos) &&
52 dev_read_block(dev, probe_blk, pos))
53 return true;
54
55 if (equal_blk(dev, stamp_blk, probe_blk))
56 return false;
57
58 /* Save time with certainly damaged blocks. */
59 if (!similar_blk(dev, stamp_blk, probe_blk, 8)) {
60 /* The probe block is damaged. */
61 return true;
62 }
63
64 /* The probe block seems to be damaged.
65 * Trying a second time...
66 */
67 return dev_write_and_reset(dev, stamp_blk, pos) ||
68 dev_read_block(dev, probe_blk, pos) ||
69 !equal_blk(dev, stamp_blk, probe_blk);
70 }
71
72 /* Minimum size of the memory chunk used to build flash drives.
73 * It must be a power of two.
74 */
75 static inline uint64_t initial_high_bit_block(struct device *dev)
76 {
77 int block_order = dev_get_block_order(dev);
78 assert(block_order <= 20);
79 return 1ULL << (20 - block_order);
80 }
81
82 /* Caller must guarantee that the left bock is good, and written. */
83 static int search_wrap(struct device *dev,
84 uint64_t left_pos, uint64_t *pright_pos,
85 const char *stamp_blk, char *probe_blk)
86 {
87 uint64_t high_bit = initial_high_bit_block(dev);
88 uint64_t pos = high_bit + left_pos;
89
90 /* The left block must be in the first memory chunk. */
91 assert(left_pos < high_bit);
92
93 /* Check that the drive has at least one memory chunk. */
94 assert((high_bit - 1) <= *pright_pos);
95
96 while (pos < *pright_pos) {
97 if (dev_read_block(dev, probe_blk, pos) &&
98 dev_read_block(dev, probe_blk, pos))
99 return true;
100 /* XXX Deal with flipped bit on reception. */
101 if (equal_blk(dev, stamp_blk, probe_blk)) {
102 /* XXX Test wraparound hypothesis. */
103 *pright_pos = high_bit - 1;
104 return false;
105 }
106 high_bit <<= 1;
107 pos = high_bit + left_pos;
108 }
109
110 return false;
111 }
112
113 #define MAX_N_BLOCK_ORDER 10
114
115 static uint64_t estimate_best_n_block(struct device *dev)
116 {
117 uint64_t write_count, write_time_us;
118 uint64_t reset_count, reset_time_us;
119 double t_w_us, t_2w_us, t_r_us;
120 uint64_t n_block_order;
121
122 perf_device_sample(dev, NULL, NULL, &write_count, &write_time_us,
123 &reset_count, &reset_time_us);
124 if (write_count < 3 || reset_count < 2) {
125 /* There is not enough measurements. */
126 return (1 << 2) - 1;
127 }
128
129 /* Let 2^n be the total number of blocks on the drive.
130 * Let p be the total number of passes.
131 * Let w = (2^m - 1) be the number of blocks written on each pass,
132 * where m >= 1.
133 *
134 * A pass is an iteration of the loop in search_edge(), that is,
135 * a call to write_test_blocks(), dev_reset(), and probe_test_blocks().
136 *
137 * The reason to have w = (2^m - 1) instead of w = 2^m is because
138 * the former leads to a clean relationship between n, p, and m
139 * when m is constant: 2^n / (w + 1)^p = 1 => p = n/m
140 *
141 * Let Tr be the time to reset the device.
142 * Let Tw be the time to write a block to @dev.
143 * Let Tw' be the time to write a block to the underlying device
144 * of @dev, that is, without overhead due to chaining multiple
145 * struct device. For example, when struct safe_device is used
146 * Tw > Tw'.
147 * Let Trd be the time to read a block from @dev.
148 *
149 * Notice that each single-block pass reduces the search space in half,
150 * and that to reduce the search space in half writing blocks,
151 * one has to increase m of one.
152 *
153 * Thus, in order to be better writing more blocks than
154 * going for another pass, the following relation must be true:
155 *
156 * Tr + Tw + Tw' >= (w - 1)(Tw + Tw')
157 *
158 * The relation above assumes Trd = 0.
159 *
160 * The left side of the relation above is the time to do _another_
161 * pass writing a single block, whereas the right side is the time to
162 * stay in the same pass and write (w - 1) more blocks.
163 * In order words, if there is no advantage to write more blocks,
164 * we stick to single-block passes.
165 *
166 * Tw' is there to account for any operation that writes
167 * the blocks back (e.g. using struct safe_device), otherwise
168 * processing operations related per written blocks that is not
169 * being accounted for (e.g. reading the blocks back to test).
170 *
171 * Solving the relation for w: w <= Tr/(Tw + Tw') + 2
172 *
173 * However, we are not interested in any w, but only those of
174 * of the form (2^m - 1) to make sure that we are not better off
175 * calling another pass. Thus, solving the previous relation for m:
176 *
177 * m <= log_2(Tr/(Tw + Tw') + 3)
178 *
179 * We approximate Tw' making it equal to Tw.
180 */
181 t_w_us = (double)write_time_us / write_count;
182 t_r_us = (double)reset_time_us / reset_count;
183 t_2w_us = t_w_us > 0. ? 2. * t_w_us : 1.; /* Avoid zero division. */
184 n_block_order = ilog2(round(t_r_us / t_2w_us + 3.));
185
186 /* Bound the maximum number of blocks per pass to limit
187 * the necessary amount of memory struct safe_device pre-allocates.
188 */
189 if (n_block_order > MAX_N_BLOCK_ORDER)
190 n_block_order = MAX_N_BLOCK_ORDER;
191
192 return (1 << n_block_order) - 1;
193 }
194
195 /* Write blocks whose offsets are after @left_pos but
196 * less or equal to @right_pos.
197 */
198 static int write_test_blocks(struct device *dev, const char *stamp_blk,
199 uint64_t left_pos, uint64_t right_pos,
200 uint64_t *pa, uint64_t *pb, uint64_t *pmax_idx)
201 {
202 uint64_t pos, last_pos;
203 uint64_t n_block = estimate_best_n_block(dev);
204
205 assert(n_block >= 1);
206
207 /* Find coeficients of function a*idx + b where idx <= max_idx. */
208 assert(left_pos < right_pos);
209 *pb = left_pos + 1;
210 *pa = round((right_pos - *pb) / (n_block + 1.));
211 *pa = !*pa ? 1ULL : *pa;
212 *pmax_idx = (right_pos - *pb) / *pa;
213 if (*pmax_idx >= n_block) {
214 /* Shift the zero of the function to the right.
215 * This avoids picking the leftmost block when a more
216 * informative block to the right is available.
217 * This also biases toward righter blocks,
218 * what improves the time to test good flash drives.
219 */
220 *pb += *pa;
221
222 *pmax_idx = n_block - 1;
223 }
224 last_pos = *pa * *pmax_idx + *pb;
225 assert(last_pos <= right_pos);
226
227 /* Write test blocks. */
228 for (pos = *pb; pos <= last_pos; pos += *pa)
229 if (dev_write_block(dev, stamp_blk, pos) &&
230 dev_write_block(dev, stamp_blk, pos))
231 return true;
232 return false;
233 }
234
235 /* Return true if the test block at @pos is damaged. */
236 static int test_test_block(struct device *dev,
237 const char *stamp_blk, char *probe_blk, uint64_t pos)
238 {
239 if (dev_read_block(dev, probe_blk, pos) &&
240 dev_read_block(dev, probe_blk, pos))
241 return true;
242
243 return !equal_blk(dev, stamp_blk, probe_blk);
244 }
245
246 static int probe_test_blocks(struct device *dev,
247 const char *stamp_blk, char *probe_blk,
248 uint64_t *pleft_pos, uint64_t *pright_pos,
249 uint64_t a, uint64_t b, uint64_t max_idx)
250 {
251 /* Signed variables. */
252 int64_t left_idx = 0;
253 int64_t right_idx = max_idx;
254 int64_t idx = right_idx;
255 while (left_idx <= right_idx) {
256 uint64_t pos = a * idx + b;
257 if (test_test_block(dev, stamp_blk, probe_blk, pos)) {
258 right_idx = idx - 1;
259 *pright_pos = pos;
260 } else {
261 left_idx = idx + 1;
262 *pleft_pos = pos;
263 }
264 idx = (left_idx + right_idx) / 2;
265 }
266 return false;
267 }
268
269 /* Caller must guarantee that the left bock is good, and written. */
270 static int search_edge(struct device *dev,
271 uint64_t *pleft_pos, uint64_t right_pos,
272 const char *stamp_blk, char *probe_blk)
273 {
274 uint64_t gap = right_pos - *pleft_pos;
275 uint64_t prv_gap = gap + 1;
276 while (prv_gap > gap && gap >= 1) {
277 uint64_t a, b, max_idx;
278 if (write_test_blocks(dev, stamp_blk, *pleft_pos, right_pos,
279 &a, &b, &max_idx))
280 return true;
281 /* Reset. */
282 if (dev_reset(dev) && dev_reset(dev))
283 return true;
284 if (probe_test_blocks(dev, stamp_blk, probe_blk,
285 pleft_pos, &right_pos, a, b, max_idx))
286 return true;
287
288 prv_gap = gap;
289 gap = right_pos - *pleft_pos;
290 }
291 return false;
292 }
293
294 /* XXX Write random data to make it harder for fake chips to become "smarter".
295 * There would be a random seed.
296 * Buffer cannot be all 0x00 or all 0xFF.
297 */
298 static void fill_buffer(char *buf, int len)
299 {
300 memset(buf, 0xAA, len);
301 }
302
303 int probe_device_max_blocks(struct device *dev)
304 {
305 uint64_t num_blocks = dev_get_size_byte(dev) >>
306 dev_get_block_order(dev);
307 int n = ceiling_log2(num_blocks);
308
309 /* Make sure that there is no overflow in the formula below.
310 * The number 10 is arbitrary here, that is, it's not tight.
311 */
312 assert(MAX_N_BLOCK_ORDER < sizeof(int) - 10);
313
314 return
315 /* search_wrap() */
316 1 +
317 /* Search_edge()
318 *
319 * The number of used blocks is (p * w); see comments in
320 * estimate_best_n_block() for the definition of the variables.
321 *
322 * p * w = n/m * (2^m - 1) < n/m * 2^m = n * (2^m / m)
323 *
324 * Let f(m) be 2^m / m. One can prove that f(m + 1) >= f(m)
325 * for all m >= 1. Therefore, the following bound is true.
326 *
327 * p * w < n * f(max_m)
328 */
329 ((n << MAX_N_BLOCK_ORDER) / MAX_N_BLOCK_ORDER);
330 }
331
332 /* XXX Properly handle read and write errors.
333 * Review each assert to check if them can be removed.
334 */
335 int probe_device(struct device *dev, uint64_t *preal_size_byte,
336 uint64_t *pannounced_size_byte, int *pwrap, int *pblock_order)
337 {
338 uint64_t dev_size_byte = dev_get_size_byte(dev);
339 const int block_size = dev_get_block_size(dev);
340 const int block_order = dev_get_block_order(dev);
341 char stack[511 + (2 << block_order)];
342 char *stamp_blk, *probe_blk;
343 /* XXX Don't write at the very beginning of the card to avoid
344 * losing the partition table.
345 * But write at a random locations to make harder for fake chips
346 * to become "smarter".
347 * And try a couple of blocks if they keep failing.
348 */
349 uint64_t left_pos = 10;
350 uint64_t right_pos = (dev_size_byte >> block_order) - 1;
351 struct device *pdev;
352
353 assert(dev_size_byte % block_size == 0);
354 assert(left_pos < right_pos);
355
356 pdev = create_perf_device(dev);
357 if (!pdev)
358 return -ENOMEM;
359
360 /* Aligning these pointers is necessary to directly read and write
361 * the block device.
362 * For the file device, this is superfluous.
363 */
364 stamp_blk = align_512(stack);
365 probe_blk = stamp_blk + block_size;
366
367 fill_buffer(stamp_blk, block_size);
368
369 /* Make sure that there is at least a good block at the beginning
370 * of the drive.
371 */
372 if (test_block(pdev, stamp_blk, probe_blk, left_pos))
373 goto bad;
374
375 if (search_wrap(pdev, left_pos, &right_pos, stamp_blk, probe_blk))
376 goto bad;
377
378 if (search_edge(pdev, &left_pos, right_pos, stamp_blk, probe_blk))
379 goto bad;
380
381 *preal_size_byte = (left_pos + 1) << block_order;
382 *pannounced_size_byte = dev_size_byte;
383 *pwrap = ceiling_log2(right_pos << block_order);
384 *pblock_order = block_order;
385 goto out;
386
387 bad:
388 *preal_size_byte = 0;
389 *pannounced_size_byte = dev_size_byte;
390 *pwrap = ceiling_log2(dev_size_byte);
391 *pblock_order = block_order;
392
393 out:
394 pdev_detach_and_free(pdev);
395 return 0;
396 }
0 #ifndef HEADER_LIBPROBE_H
1 #define HEADER_LIBPROBE_H
2
3 #include <stdint.h>
4
5 #include "libdevs.h"
6
7 int probe_device_max_blocks(struct device *dev);
8
9 int probe_device(struct device *dev, uint64_t *preal_size_byte,
10 uint64_t *pannounced_size_byte, int *pwrap, int *block_order);
11
12 #endif /* HEADER_LIBPROBE_H */
0 #include "libutils.h"
1
2 /* Count the number of 1 bits. */
3 static int pop(uint64_t x)
4 {
5 int n = 0;
6 while (x) {
7 n++;
8 x = x & (x - 1);
9 }
10 return n;
11 }
12
13 int ilog2(uint64_t x)
14 {
15 x = x | (x >> 1);
16 x = x | (x >> 2);
17 x = x | (x >> 4);
18 x = x | (x >> 8);
19 x = x | (x >> 16);
20 x = x | (x >> 32);
21 return pop(x) - 1;
22 }
23
24 /* Least power of 2 greater than or equal to x. */
25 static uint64_t clp2(uint64_t x)
26 {
27 x = x - 1;
28 x = x | (x >> 1);
29 x = x | (x >> 2);
30 x = x | (x >> 4);
31 x = x | (x >> 8);
32 x = x | (x >> 16);
33 x = x | (x >> 32);
34 return x + 1;
35 }
36
37 int ceiling_log2(uint64_t x)
38 {
39 return ilog2(clp2(x));
40 }
0 #ifndef HEADER_LIBUTILS_H
1 #define HEADER_LIBUTILS_H
2
3 #include <stdint.h>
4
5 int ilog2(uint64_t x);
6 int ceiling_log2(uint64_t x);
7
8 static inline void *align_512(void *p)
9 {
10 uintptr_t ip = (uintptr_t)p;
11 return (void *)( (ip + 511) & ~511 );
12 }
13
14 #endif /* HEADER_LIBUTILS_H */
00 #define _GNU_SOURCE
1
2 #if __APPLE__ && __MACH__
3
4 #define _DARWIN_C_SOURCE
5
6 #include <fcntl.h> /* For function fcntl. */
7
8 #endif /* Apple Macintosh */
19
210 #include <stdio.h>
311 #include <stdlib.h>
412 #include <string.h>
13 #include <ctype.h>
14 #include <assert.h>
15 #include <limits.h>
516 #include <sys/types.h>
617 #include <dirent.h>
718 #include <errno.h>
819 #include <err.h>
920
21 #include "version.h"
1022 #include "utils.h"
1123
1224 const char *adjust_unit(double *ptr_bytes)
208220 void print_header(FILE *f, const char *name)
209221 {
210222 fprintf(f,
211 "F3 %s 4.0\n"
223 "F3 %s " F3_STR_VERSION "\n"
212224 "Copyright (C) 2010 Digirati Internet LTDA.\n"
213225 "This is free software; see the source for copying conditions.\n"
214226 "\n", name);
215227 }
228
229 #if __APPLE__ && __MACH__
230
231 /* This function is a _rough_ approximation of fdatasync(2). */
232 int fdatasync(int fd)
233 {
234 return fcntl(fd, F_FULLFSYNC);
235 }
236
237 /* This function is a _rough_ approximation of posix_fadvise(2). */
238 int posix_fadvise(int fd, off_t offset, off_t len, int advice)
239 {
240 UNUSED(offset);
241 UNUSED(len);
242 switch (advice) {
243 case POSIX_FADV_SEQUENTIAL:
244 return fcntl(fd, F_RDAHEAD, 1);
245 case POSIX_FADV_DONTNEED:
246 return fcntl(fd, F_NOCACHE, 1);
247 default:
248 assert(0);
249 }
250 }
251
252 #endif /* Apple Macintosh */
00 #ifndef HEADER_UTILS_H
11 #define HEADER_UTILS_H
22
3 #include <ctype.h>
4 #include <assert.h>
5 #include <sys/time.h>
6 #include <limits.h>
7 #include <stdint.h>
3 #include <stdio.h> /* For type FILE. */
4 #include <sys/time.h> /* For struct timeval. */
5 #include <stdint.h> /* For type uint64_t. */
86
97 #define SECTOR_SIZE (512)
108 #define GIGABYTES (1024 * 1024 * 1024)
3533 return prv_number * 4294967311ULL + 17;
3634 }
3735
36 #define UNUSED(x) ((void)x)
37
3838 #if __APPLE__ && __MACH__
3939
40 /* For function fcntl. */
41 #include <fcntl.h>
42 /* For type off_t. */
43 #include <unistd.h>
44
45 /* This function is a _rough_ approximation of fdatasync(2). */
46 static inline int fdatasync(int fd)
47 {
48 return fcntl(fd, F_FULLFSYNC);
49 }
40 #include <unistd.h> /* For type off_t. */
5041
5142 #define POSIX_FADV_SEQUENTIAL 2 /* Expect sequential page references. */
5243 #define POSIX_FADV_DONTNEED 4 /* Don't need these pages. */
5344
54 /* This function is a _rough_ approximation of posix_fadvise(2). */
55 static inline int posix_fadvise(int fd, off_t offset, off_t len, int advice)
56 {
57 switch (advice) {
58 case POSIX_FADV_SEQUENTIAL:
59 return fcntl(fd, F_RDAHEAD, 1);
60 case POSIX_FADV_DONTNEED:
61 return fcntl(fd, F_NOCACHE, 1);
62 default:
63 assert(0);
64 }
65 }
45 int fdatasync(int fd);
46 int posix_fadvise(int fd, off_t offset, off_t len, int advice);
6647
6748 #endif /* Apple Macintosh */
6849
0 #ifndef HEADER_VERSION_H
1 #define HEADER_VERSION_H
2
3 #define F3_STR_VERSION "5.0"
4
5 #endif /* HEADER_VERSION_H */