2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License v2 as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 * General Public License for more details.
11 * You should have received a copy of the GNU General Public
12 * License along with this program; if not, write to the
13 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14 * Boston, MA 021110-1307, USA.
17 #include "kerncompat.h"
21 #include <sys/fcntl.h>
27 #include <kernel-lib/crc32c.h>
30 #define BLOCKSIZE (4096)
31 static char buf
[BLOCKSIZE
];
34 static int check_csum_superblock(void *sb
)
39 crc
= btrfs_csum_data((char *)sb
+ BTRFS_CSUM_SIZE
,
40 crc
, BTRFS_SUPER_INFO_SIZE
- BTRFS_CSUM_SIZE
);
41 btrfs_csum_final(crc
, result
);
43 return !memcmp(sb
, &result
, csum_size
);
46 static void update_block_csum(void *block
, int is_sb
)
49 struct btrfs_header
*hdr
;
53 crc
= btrfs_csum_data((char *)block
+ BTRFS_CSUM_SIZE
, crc
,
54 BTRFS_SUPER_INFO_SIZE
- BTRFS_CSUM_SIZE
);
56 crc
= btrfs_csum_data((char *)block
+ BTRFS_CSUM_SIZE
, crc
,
57 BLOCKSIZE
- BTRFS_CSUM_SIZE
);
59 btrfs_csum_final(crc
, result
);
60 memset(block
, 0, BTRFS_CSUM_SIZE
);
61 hdr
= (struct btrfs_header
*)block
;
62 memcpy(&hdr
->csum
, result
, csum_size
);
65 static u64
arg_strtou64(const char *str
)
68 char *ptr_parse_end
= NULL
;
70 value
= strtoull(str
, &ptr_parse_end
, 0);
71 if (ptr_parse_end
&& *ptr_parse_end
!= '\0') {
72 fprintf(stderr
, "ERROR: %s is not a valid numeric value.\n",
78 * if we pass a negative number to strtoull, it will return an
79 * unexpected number to us, so let's do the check ourselves.
82 fprintf(stderr
, "ERROR: %s: negative value is invalid.\n",
86 if (value
== ULLONG_MAX
) {
87 fprintf(stderr
, "ERROR: %s is too large.\n", str
);
100 OP_NAND
, /* broken */
118 enum field_type type
;
120 { .name
= "total_bytes", .type
= TYPE_U64
},
121 { .name
= "root", .type
= TYPE_U64
},
122 { .name
= "generation", .type
= TYPE_U64
},
123 { .name
= "chunk_root", .type
= TYPE_U64
},
124 { .name
= "chunk_root_generation", .type
= TYPE_U64
},
125 { .name
= "cache_generation", .type
= TYPE_U64
},
126 { .name
= "uuid_tree_generation", .type
= TYPE_U64
},
127 { .name
= "compat_flags", .type
= TYPE_U64
},
128 { .name
= "compat_ro_flags", .type
= TYPE_U64
},
129 { .name
= "incompat_flags", .type
= TYPE_U64
},
130 { .name
= "csum_type", .type
= TYPE_U16
},
133 #define MOD_FIELD_XX(fname, set, val, bits, f_dec, f_hex, f_type) \
134 else if (strcmp(name, #fname) == 0) { \
136 printf("SET: "#fname" "f_dec" (0x"f_hex")\n", \
137 (f_type)*val, (f_type)*val); \
138 sb->fname = cpu_to_le##bits(*val); \
140 *val = le##bits##_to_cpu(sb->fname); \
141 printf("GET: "#fname" "f_dec" (0x"f_hex")\n", \
142 (f_type)*val, (f_type)*val); \
146 #define MOD_FIELD64(fname, set, val) \
147 MOD_FIELD_XX(fname, set, val, 64, "%llu", "%llx", unsigned long long)
150 #define MOD_FIELD(fname, set, val) MOD_FIELD64(fname, set, val)
153 * Support only GET and SET properly, ADD and SUB may work
155 #define MOD_FIELD32(fname, set, val) \
156 MOD_FIELD_XX(fname, set, val, 32, "%u", "%x", unsigned int)
158 #define MOD_FIELD16(fname, set, val) \
159 MOD_FIELD_XX(fname, set, val, 16, "%hu", "%hx", unsigned short int)
161 #define MOD_FIELD8(fname, set, val) \
162 MOD_FIELD_XX(fname, set, val, 8, "%hhu", "%hhx", unsigned char)
164 static void mod_field_by_name(struct btrfs_super_block
*sb
, int set
, const char *name
,
168 MOD_FIELD(total_bytes
, set
, val
)
169 MOD_FIELD(root
, set
, val
)
170 MOD_FIELD(generation
, set
, val
)
171 MOD_FIELD(chunk_root
, set
, val
)
172 MOD_FIELD(chunk_root_generation
, set
, val
)
173 MOD_FIELD(cache_generation
, set
, val
)
174 MOD_FIELD(uuid_tree_generation
, set
, val
)
175 MOD_FIELD(compat_flags
, set
, val
)
176 MOD_FIELD(compat_ro_flags
, set
, val
)
177 MOD_FIELD(incompat_flags
, set
, val
)
178 MOD_FIELD16(csum_type
, set
, val
)
180 printf("ERROR: unhandled field: %s\n", name
);
185 static int sb_edit(struct btrfs_super_block
*sb
, struct fspec
*fsp
)
190 mod_field_by_name(sb
, 0, fsp
->name
, &val
);
192 case OP_GET
: newval
= val
; break;
193 case OP_SET
: newval
= fsp
->value
; break;
194 case OP_ADD
: newval
= val
+ fsp
->value
; break;
195 case OP_SUB
: newval
= val
- fsp
->value
; break;
196 case OP_XOR
: newval
= val
^ fsp
->value
; break;
197 case OP_NAND
: newval
= val
& (~fsp
->value
); break;
198 case OP_BSWAP
: newval
= bswap_64(val
); break;
199 default: printf("ERROR: unhandled operation: %d\n", fsp
->fop
); exit(1);
202 mod_field_by_name(sb
, 1, fsp
->name
, &newval
);
207 static int is_known_field(const char *f
)
211 for (i
= 0; i
< ARRAY_SIZE(known_fields
); i
++)
212 if (strcmp(f
, known_fields
[i
].name
) == 0)
217 static int arg_to_op_value(const char *arg
, enum field_op
*op
, u64
*val
)
222 case '?': *op
= OP_GET
; *val
= 0; break;
223 case '=': *op
= OP_SET
; *val
= arg_strtou64(arg
+ 1); break;
224 case '+': *op
= OP_ADD
; *val
= arg_strtou64(arg
+ 1); break;
225 case '-': *op
= OP_SUB
; *val
= arg_strtou64(arg
+ 1); break;
226 case '^': *op
= OP_XOR
; *val
= arg_strtou64(arg
+ 1); break;
227 case '~': *op
= OP_NAND
; *val
= arg_strtou64(arg
+ 1); break;
228 case '@': *op
= OP_BSWAP
; *val
= arg_strtou64(arg
+ 1); break;
230 printf("ERROR: unknown op: %c\n", arg
[0]);
237 int main(int argc
, char **argv
) {
241 struct btrfs_header
*hdr
;
242 struct btrfs_super_block
*sb
;
244 struct fspec spec
[128];
248 memset(spec
, 0, sizeof(spec
));
250 printf("Usage: %s image [fieldspec...]\n", argv
[0]);
253 fd
= open(argv
[1], O_RDWR
| O_EXCL
);
259 /* verify superblock */
260 csum_size
= btrfs_csum_sizes
[BTRFS_CSUM_TYPE_CRC32
];
261 off
= BTRFS_SUPER_INFO_OFFSET
;
263 ret
= pread(fd
, buf
, BLOCKSIZE
, off
);
265 printf("pread error %d at offset %llu\n",
266 ret
, (unsigned long long)off
);
269 if (ret
!= BLOCKSIZE
) {
270 printf("pread error at offset %llu: read only %d bytes\n",
271 (unsigned long long)off
, ret
);
274 hdr
= (struct btrfs_header
*)buf
;
275 /* verify checksum */
276 if (!check_csum_superblock(&hdr
->csum
)) {
277 printf("super block checksum does not match at offset %llu, will be corrected\n",
278 (unsigned long long)off
);
280 printf("super block checksum is ok\n");
282 sb
= (struct btrfs_super_block
*)buf
;
286 for (i
= 2; i
< argc
; i
++) {
290 printf("ERROR: bad argument count\n");
295 if (!is_known_field(argv
[i
])) {
296 printf("ERROR: unknown filed: %s\n", argv
[i
]);
302 f
->name
= strdup(argv
[i
]);
304 if (arg_to_op_value(argv
[i
], &f
->fop
, &f
->value
)) {
310 for (i
= 0; i
< specidx
; i
++) {
311 sb_edit(sb
, &spec
[i
]);
316 printf("Update csum\n");
317 update_block_csum(buf
, 1);
318 ret
= pwrite(fd
, buf
, BLOCKSIZE
, off
);
320 printf("pwrite error %d at offset %llu\n",
321 ret
, (unsigned long long)off
);
324 if (ret
!= BLOCKSIZE
) {
325 printf("pwrite error at offset %llu: written only %d bytes\n",
326 (unsigned long long)off
, ret
);
331 printf("Nothing changed\n");