2009-03-28 07:43:01 +05:30
|
|
|
/* vi: set sw=4 ts=4: */
|
|
|
|
/*
|
|
|
|
* mkfs_vfat: utility to create FAT32 filesystem
|
|
|
|
* inspired by dosfstools
|
|
|
|
*
|
|
|
|
* Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
|
|
|
|
*
|
2010-08-16 23:44:46 +05:30
|
|
|
* Licensed under GPLv2, see file LICENSE in this source tree.
|
2009-03-28 07:43:01 +05:30
|
|
|
*/
|
2016-11-16 20:48:50 +05:30
|
|
|
//config:config MKDOSFS
|
2018-12-28 07:50:17 +05:30
|
|
|
//config: bool "mkdosfs (7.2 kb)"
|
2016-11-16 20:48:50 +05:30
|
|
|
//config: default y
|
|
|
|
//config: select PLATFORM_LINUX
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Utility to create FAT32 filesystems.
|
2016-11-16 20:48:50 +05:30
|
|
|
//config:
|
|
|
|
//config:config MKFS_VFAT
|
2018-12-28 07:50:17 +05:30
|
|
|
//config: bool "mkfs.vfat (7.2 kb)"
|
2016-11-16 20:48:50 +05:30
|
|
|
//config: default y
|
|
|
|
//config: select PLATFORM_LINUX
|
|
|
|
//config: help
|
2017-07-21 13:20:55 +05:30
|
|
|
//config: Alias to "mkdosfs".
|
2016-11-16 20:48:50 +05:30
|
|
|
|
2017-01-29 19:27:33 +05:30
|
|
|
// APPLET_ODDNAME:name main location suid_type help
|
|
|
|
//applet:IF_MKDOSFS( APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
|
2016-11-16 20:48:50 +05:30
|
|
|
//applet:IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
|
|
|
|
|
|
|
|
//kbuild:lib-$(CONFIG_MKDOSFS) += mkfs_vfat.o
|
|
|
|
//kbuild:lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o
|
2011-04-11 06:59:49 +05:30
|
|
|
|
|
|
|
//usage:#define mkfs_vfat_trivial_usage
|
|
|
|
//usage: "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
|
|
|
|
/* Accepted but ignored:
|
|
|
|
"[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] "
|
|
|
|
"[-m boot-msg-file] [-i volume-id] "
|
|
|
|
"[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] "
|
|
|
|
"[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] "
|
|
|
|
*/
|
|
|
|
//usage:#define mkfs_vfat_full_usage "\n\n"
|
|
|
|
//usage: "Make a FAT32 filesystem\n"
|
|
|
|
/* //usage: "\n -c Check device for bad blocks" */
|
|
|
|
//usage: "\n -v Verbose"
|
|
|
|
/* //usage: "\n -I Allow to use entire disk device (e.g. /dev/hda)" */
|
|
|
|
//usage: "\n -n LBL Volume label"
|
|
|
|
|
2009-07-10 22:07:06 +05:30
|
|
|
#include "libbb.h"
|
|
|
|
|
2009-03-28 07:43:01 +05:30
|
|
|
#include <linux/hdreg.h> /* HDIO_GETGEO */
|
|
|
|
#include <linux/fd.h> /* FDGETPRM */
|
2009-07-08 06:28:38 +05:30
|
|
|
#include <sys/mount.h> /* BLKSSZGET */
|
|
|
|
#if !defined(BLKSSZGET)
|
|
|
|
# define BLKSSZGET _IO(0x12, 104)
|
|
|
|
#endif
|
2009-03-28 07:43:01 +05:30
|
|
|
//#include <linux/msdos_fs.h>
|
|
|
|
|
|
|
|
#define SECTOR_SIZE 512
|
|
|
|
|
|
|
|
#define SECTORS_PER_BLOCK (BLOCK_SIZE / SECTOR_SIZE)
|
|
|
|
|
|
|
|
// M$ says the high 4 bits of a FAT32 FAT entry are reserved
|
|
|
|
#define EOF_FAT32 0x0FFFFFF8
|
|
|
|
#define BAD_FAT32 0x0FFFFFF7
|
|
|
|
#define MAX_CLUST_32 0x0FFFFFF0
|
|
|
|
|
|
|
|
#define ATTR_VOLUME 8
|
|
|
|
|
2010-10-28 22:27:19 +05:30
|
|
|
#define NUM_FATS 2
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
/* FAT32 filesystem looks like this:
|
|
|
|
* sector -nn...-1: "hidden" sectors, all sectors before this partition
|
|
|
|
* (-h hidden-sectors sets it. Useful only for boot loaders,
|
|
|
|
* they need to know _disk_ offset in order to be able to correctly
|
|
|
|
* address sectors relative to start of disk)
|
|
|
|
* sector 0: boot sector
|
|
|
|
* sector 1: info sector
|
|
|
|
* sector 2: set aside for boot code which didn't fit into sector 0
|
|
|
|
* ...(zero-filled sectors)...
|
|
|
|
* sector B: backup copy of sector 0 [B set by -b backup-boot-sector]
|
|
|
|
* sector B+1: backup copy of sector 1
|
|
|
|
* sector B+2: backup copy of sector 2
|
|
|
|
* ...(zero-filled sectors)...
|
|
|
|
* sector R: FAT#1 [R set by -R reserved-sectors]
|
|
|
|
* ...(FAT#1)...
|
|
|
|
* sector R+fat_size: FAT#2
|
|
|
|
* ...(FAT#2)...
|
|
|
|
* sector R+fat_size*2: cluster #2
|
|
|
|
* ...(cluster #2)...
|
|
|
|
* sector R+fat_size*2+clust_size: cluster #3
|
|
|
|
* ...(the rest is filled by clusters till the end)...
|
|
|
|
*/
|
|
|
|
|
|
|
|
enum {
|
|
|
|
// Perhaps this should remain constant
|
|
|
|
info_sector_number = 1,
|
|
|
|
// TODO: make these cmdline options
|
|
|
|
// dont forget sanity check: backup_boot_sector + 3 <= reserved_sect
|
|
|
|
backup_boot_sector = 3,
|
|
|
|
reserved_sect = 6,
|
|
|
|
};
|
|
|
|
|
|
|
|
// how many blocks we try to read while testing
|
|
|
|
#define TEST_BUFFER_BLOCKS 16
|
|
|
|
|
|
|
|
struct msdos_dir_entry {
|
|
|
|
char name[11]; /* 000 name and extension */
|
|
|
|
uint8_t attr; /* 00b attribute bits */
|
|
|
|
uint8_t lcase; /* 00c case for base and extension */
|
|
|
|
uint8_t ctime_cs; /* 00d creation time, centiseconds (0-199) */
|
|
|
|
uint16_t ctime; /* 00e creation time */
|
|
|
|
uint16_t cdate; /* 010 creation date */
|
|
|
|
uint16_t adate; /* 012 last access date */
|
|
|
|
uint16_t starthi; /* 014 high 16 bits of cluster in FAT32 */
|
|
|
|
uint16_t time; /* 016 time */
|
|
|
|
uint16_t date; /* 018 date */
|
|
|
|
uint16_t start; /* 01a first cluster */
|
|
|
|
uint32_t size; /* 01c file size in bytes */
|
2009-09-16 04:28:11 +05:30
|
|
|
} PACKED;
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
/* Example of boot sector's beginning:
|
|
|
|
0000 eb 58 90 4d 53 57 49 4e 34 2e 31 00 02 08 26 00 |...MSWIN4.1...&.|
|
|
|
|
0010 02 00 00 00 00 f8 00 00 3f 00 ff 00 3f 00 00 00 |........?...?...|
|
|
|
|
0020 54 9b d0 00 0d 34 00 00 00 00 00 00 02 00 00 00 |T....4..........|
|
|
|
|
0030 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
|
|
|
|
0040 80 00 29 71 df 51 e0 4e 4f 20 4e 41 4d 45 20 20 |..)q.Q.NO NAME |
|
|
|
|
0050 20 20 46 41 54 33 32 20 20 20 33 c9 8e d1 bc f4 | FAT32 3.....|
|
|
|
|
*/
|
|
|
|
struct msdos_volume_info { /* (offsets are relative to start of boot sector) */
|
|
|
|
uint8_t drive_number; /* 040 BIOS drive number */
|
|
|
|
uint8_t reserved; /* 041 unused */
|
|
|
|
uint8_t ext_boot_sign; /* 042 0x29 if fields below exist (DOS 3.3+) */
|
|
|
|
uint32_t volume_id32; /* 043 volume ID number */
|
|
|
|
char volume_label[11];/* 047 volume label */
|
|
|
|
char fs_type[8]; /* 052 typically "FATnn" */
|
2009-09-16 04:28:11 +05:30
|
|
|
} PACKED; /* 05a end. Total size 26 (0x1a) bytes */
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
struct msdos_boot_sector {
|
2010-06-04 19:25:17 +05:30
|
|
|
/* We use strcpy to fill both, and gcc-4.4.x complains if they are separate */
|
|
|
|
char boot_jump_and_sys_id[3+8]; /* 000 short or near jump instruction */
|
|
|
|
/*char system_id[8];*/ /* 003 name - can be used to special case partition manager volumes */
|
2009-03-28 07:43:01 +05:30
|
|
|
uint16_t bytes_per_sect; /* 00b bytes per logical sector */
|
|
|
|
uint8_t sect_per_clust; /* 00d sectors/cluster */
|
|
|
|
uint16_t reserved_sect; /* 00e reserved sectors (sector offset of 1st FAT relative to volume start) */
|
|
|
|
uint8_t fats; /* 010 number of FATs */
|
|
|
|
uint16_t dir_entries; /* 011 root directory entries */
|
|
|
|
uint16_t volume_size_sect; /* 013 volume size in sectors */
|
|
|
|
uint8_t media_byte; /* 015 media code */
|
|
|
|
uint16_t sect_per_fat; /* 016 sectors/FAT */
|
|
|
|
uint16_t sect_per_track; /* 018 sectors per track */
|
|
|
|
uint16_t heads; /* 01a number of heads */
|
|
|
|
uint32_t hidden; /* 01c hidden sectors (sector offset of volume within physical disk) */
|
|
|
|
uint32_t fat32_volume_size_sect; /* 020 volume size in sectors (if volume_size_sect == 0) */
|
|
|
|
uint32_t fat32_sect_per_fat; /* 024 sectors/FAT */
|
|
|
|
uint16_t fat32_flags; /* 028 bit 8: fat mirroring, low 4: active fat */
|
|
|
|
uint8_t fat32_version[2]; /* 02a major, minor filesystem version (I see 0,0) */
|
|
|
|
uint32_t fat32_root_cluster; /* 02c first cluster in root directory */
|
|
|
|
uint16_t fat32_info_sector; /* 030 filesystem info sector (usually 1) */
|
|
|
|
uint16_t fat32_backup_boot; /* 032 backup boot sector (usually 6) */
|
|
|
|
uint32_t reserved2[3]; /* 034 unused */
|
|
|
|
struct msdos_volume_info vi; /* 040 */
|
|
|
|
char boot_code[0x200 - 0x5a - 2]; /* 05a */
|
|
|
|
#define BOOT_SIGN 0xAA55
|
|
|
|
uint16_t boot_sign; /* 1fe */
|
2009-09-16 04:28:11 +05:30
|
|
|
} PACKED;
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
#define FAT_FSINFO_SIG1 0x41615252
|
|
|
|
#define FAT_FSINFO_SIG2 0x61417272
|
|
|
|
struct fat32_fsinfo {
|
|
|
|
uint32_t signature1; /* 0x52,0x52,0x41,0x61, "RRaA" */
|
|
|
|
uint32_t reserved1[128 - 8];
|
|
|
|
uint32_t signature2; /* 0x72,0x72,0x61,0x41, "rrAa" */
|
|
|
|
uint32_t free_clusters; /* free cluster count. -1 if unknown */
|
|
|
|
uint32_t next_cluster; /* most recently allocated cluster */
|
|
|
|
uint32_t reserved2[3];
|
|
|
|
uint16_t reserved3; /* 1fc */
|
|
|
|
uint16_t boot_sign; /* 1fe */
|
2009-09-16 04:28:11 +05:30
|
|
|
} PACKED;
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
struct bug_check {
|
|
|
|
char BUG1[sizeof(struct msdos_dir_entry ) == 0x20 ? 1 : -1];
|
|
|
|
char BUG2[sizeof(struct msdos_volume_info) == 0x1a ? 1 : -1];
|
|
|
|
char BUG3[sizeof(struct msdos_boot_sector) == 0x200 ? 1 : -1];
|
|
|
|
char BUG4[sizeof(struct fat32_fsinfo ) == 0x200 ? 1 : -1];
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char boot_code[] ALIGN1 =
|
|
|
|
"\x0e" /* 05a: push cs */
|
|
|
|
"\x1f" /* 05b: pop ds */
|
|
|
|
"\xbe\x77\x7c" /* write_msg: mov si, offset message_txt */
|
|
|
|
"\xac" /* 05f: lodsb */
|
|
|
|
"\x22\xc0" /* 060: and al, al */
|
|
|
|
"\x74\x0b" /* 062: jz key_press */
|
|
|
|
"\x56" /* 064: push si */
|
|
|
|
"\xb4\x0e" /* 065: mov ah, 0eh */
|
|
|
|
"\xbb\x07\x00" /* 067: mov bx, 0007h */
|
|
|
|
"\xcd\x10" /* 06a: int 10h */
|
|
|
|
"\x5e" /* 06c: pop si */
|
|
|
|
"\xeb\xf0" /* 06d: jmp write_msg */
|
|
|
|
"\x32\xe4" /* key_press: xor ah, ah */
|
|
|
|
"\xcd\x16" /* 071: int 16h */
|
|
|
|
"\xcd\x19" /* 073: int 19h */
|
|
|
|
"\xeb\xfe" /* foo: jmp foo */
|
|
|
|
/* 077: message_txt: */
|
|
|
|
"This is not a bootable disk\r\n";
|
|
|
|
|
|
|
|
|
2009-03-28 07:58:58 +05:30
|
|
|
#define MARK_CLUSTER(cluster, value) \
|
2010-05-09 03:43:40 +05:30
|
|
|
((uint32_t *)fat)[cluster] = SWAP_LE32(value)
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
/* compat:
|
|
|
|
* mkdosfs 2.11 (12 Mar 2005)
|
|
|
|
* Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file]
|
|
|
|
* [-b backup-boot-sector]
|
|
|
|
* [-m boot-msg-file] [-n volume-name] [-i volume-id]
|
|
|
|
* [-s sectors-per-cluster] [-S logical-sector-size]
|
|
|
|
* [-f number-of-FATs]
|
|
|
|
* [-h hidden-sectors] [-F fat-size] [-r root-dir-entries]
|
|
|
|
* [-R reserved-sectors]
|
|
|
|
* /dev/name [blocks]
|
|
|
|
*/
|
|
|
|
int mkfs_vfat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
|
|
|
|
int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
const char *volume_label = "";
|
|
|
|
char *buf;
|
|
|
|
char *device_name;
|
|
|
|
uoff_t volume_size_bytes;
|
|
|
|
uoff_t volume_size_sect;
|
|
|
|
uint32_t total_clust;
|
|
|
|
uint32_t volume_id;
|
|
|
|
int dev;
|
|
|
|
unsigned bytes_per_sect;
|
|
|
|
unsigned sect_per_fat;
|
2010-01-25 18:09:24 +05:30
|
|
|
unsigned opts;
|
2009-03-28 07:43:01 +05:30
|
|
|
uint16_t sect_per_track;
|
|
|
|
uint8_t media_byte;
|
|
|
|
uint8_t sect_per_clust;
|
|
|
|
uint8_t heads;
|
|
|
|
enum {
|
|
|
|
OPT_A = 1 << 0, // [IGNORED] atari format
|
|
|
|
OPT_b = 1 << 1, // [IGNORED] location of backup boot sector
|
|
|
|
OPT_c = 1 << 2, // [IGNORED] check filesystem
|
|
|
|
OPT_C = 1 << 3, // [IGNORED] create a new file
|
|
|
|
OPT_f = 1 << 4, // [IGNORED] number of FATs
|
|
|
|
OPT_F = 1 << 5, // [IGNORED, implied 32] choose FAT size
|
|
|
|
OPT_h = 1 << 6, // [IGNORED] number of hidden sectors
|
|
|
|
OPT_I = 1 << 7, // [IGNORED] don't bark at entire disk devices
|
|
|
|
OPT_i = 1 << 8, // [IGNORED] volume ID
|
|
|
|
OPT_l = 1 << 9, // [IGNORED] bad block filename
|
|
|
|
OPT_m = 1 << 10, // [IGNORED] message file
|
|
|
|
OPT_n = 1 << 11, // volume label
|
|
|
|
OPT_r = 1 << 12, // [IGNORED] root directory entries
|
|
|
|
OPT_R = 1 << 13, // [IGNORED] number of reserved sectors
|
|
|
|
OPT_s = 1 << 14, // [IGNORED] sectors per cluster
|
|
|
|
OPT_S = 1 << 15, // [IGNORED] sector size
|
|
|
|
OPT_v = 1 << 16, // verbose
|
|
|
|
};
|
|
|
|
|
2017-08-09 01:25:02 +05:30
|
|
|
opts = getopt32(argv, "^"
|
|
|
|
"Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v"
|
|
|
|
"\0" "-1", //:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c
|
2009-03-28 07:43:01 +05:30
|
|
|
NULL, NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, &volume_label, NULL, NULL, NULL, NULL);
|
|
|
|
argv += optind;
|
|
|
|
|
|
|
|
// cache device name
|
|
|
|
device_name = argv[0];
|
|
|
|
// default volume ID = creation time
|
|
|
|
volume_id = time(NULL);
|
|
|
|
|
2010-02-02 04:18:27 +05:30
|
|
|
dev = xopen(device_name, O_RDWR);
|
2010-08-31 16:12:06 +05:30
|
|
|
xfstat(dev, &st, device_name);
|
2009-03-28 07:43:01 +05:30
|
|
|
|
|
|
|
//
|
|
|
|
// Get image size and sector size
|
|
|
|
//
|
|
|
|
bytes_per_sect = SECTOR_SIZE;
|
|
|
|
if (!S_ISBLK(st.st_mode)) {
|
|
|
|
if (!S_ISREG(st.st_mode)) {
|
|
|
|
if (!argv[1])
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg_and_die("image size must be specified");
|
2009-03-28 07:43:01 +05:30
|
|
|
}
|
|
|
|
// not a block device, skip bad sectors check
|
|
|
|
opts &= ~OPT_c;
|
|
|
|
} else {
|
|
|
|
int min_bytes_per_sect;
|
|
|
|
#if 0
|
|
|
|
unsigned device_num;
|
|
|
|
// for true block devices we do check sanity
|
|
|
|
device_num = st.st_rdev & 0xff3f;
|
|
|
|
// do we allow to format the whole disk device?
|
|
|
|
if (!(opts & OPT_I) && (
|
|
|
|
device_num == 0x0300 || // hda, hdb
|
|
|
|
(device_num & 0xff0f) == 0x0800 || // sd
|
|
|
|
device_num == 0x0d00 || // xd
|
|
|
|
device_num == 0x1600 ) // hdc, hdd
|
|
|
|
)
|
2009-04-29 17:32:57 +05:30
|
|
|
bb_error_msg_and_die("will not try to make filesystem on full-disk device (use -I if wanted)");
|
2009-03-28 07:43:01 +05:30
|
|
|
// can't work on mounted filesystems
|
2009-07-18 19:52:26 +05:30
|
|
|
if (find_mount_point(device_name, 0))
|
2009-04-29 17:32:57 +05:30
|
|
|
bb_error_msg_and_die("can't format mounted filesystem");
|
2009-03-28 07:43:01 +05:30
|
|
|
#endif
|
|
|
|
// get true sector size
|
|
|
|
// (parameter must be int*, not long* or size_t*)
|
|
|
|
xioctl(dev, BLKSSZGET, &min_bytes_per_sect);
|
|
|
|
if (min_bytes_per_sect > SECTOR_SIZE) {
|
|
|
|
bytes_per_sect = min_bytes_per_sect;
|
|
|
|
bb_error_msg("for this device sector size is %u", min_bytes_per_sect);
|
|
|
|
}
|
|
|
|
}
|
2010-02-02 04:18:27 +05:30
|
|
|
volume_size_bytes = get_volume_size_in_bytes(dev, argv[1], 1024, /*extend:*/ 1);
|
2009-03-28 07:43:01 +05:30
|
|
|
volume_size_sect = volume_size_bytes / bytes_per_sect;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Find out or guess media parameters
|
|
|
|
//
|
|
|
|
media_byte = 0xf8;
|
|
|
|
heads = 255;
|
|
|
|
sect_per_track = 63;
|
|
|
|
sect_per_clust = 1;
|
|
|
|
{
|
|
|
|
struct hd_geometry geometry;
|
|
|
|
// size (in sectors), sect (per track), head
|
|
|
|
struct floppy_struct param;
|
|
|
|
|
|
|
|
// N.B. whether to use HDIO_GETGEO or HDIO_REQ?
|
|
|
|
if (ioctl(dev, HDIO_GETGEO, &geometry) == 0
|
|
|
|
&& geometry.sectors
|
|
|
|
&& geometry.heads
|
|
|
|
) {
|
|
|
|
// hard drive
|
|
|
|
sect_per_track = geometry.sectors;
|
|
|
|
heads = geometry.heads;
|
|
|
|
|
|
|
|
set_cluster_size:
|
|
|
|
/* For FAT32, try to do the same as M$'s format command
|
|
|
|
* (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
|
|
|
|
* fs size <= 260M: 0.5k clusters
|
|
|
|
* fs size <= 8G: 4k clusters
|
|
|
|
* fs size <= 16G: 8k clusters
|
|
|
|
* fs size > 16G: 16k clusters
|
|
|
|
*/
|
2009-03-29 00:38:23 +05:30
|
|
|
sect_per_clust = 1;
|
|
|
|
if (volume_size_bytes >= 260*1024*1024) {
|
|
|
|
sect_per_clust = 8;
|
|
|
|
/* fight gcc: */
|
|
|
|
/* "error: integer overflow in expression" */
|
|
|
|
/* "error: right shift count >= width of type" */
|
|
|
|
if (sizeof(off_t) > 4) {
|
|
|
|
unsigned t = (volume_size_bytes >> 31 >> 1);
|
|
|
|
if (t >= 8/4)
|
|
|
|
sect_per_clust = 16;
|
|
|
|
if (t >= 16/4)
|
|
|
|
sect_per_clust = 32;
|
|
|
|
}
|
|
|
|
}
|
2009-03-28 07:43:01 +05:30
|
|
|
} else {
|
2009-03-28 17:47:20 +05:30
|
|
|
// floppy, loop, or regular file
|
2009-03-28 07:43:01 +05:30
|
|
|
int not_floppy = ioctl(dev, FDGETPRM, ¶m);
|
|
|
|
if (not_floppy == 0) {
|
|
|
|
// floppy disk
|
|
|
|
sect_per_track = param.sect;
|
|
|
|
heads = param.head;
|
|
|
|
volume_size_sect = param.size;
|
|
|
|
volume_size_bytes = param.size * SECTOR_SIZE;
|
|
|
|
}
|
|
|
|
// setup the media descriptor byte
|
|
|
|
switch (volume_size_sect) {
|
|
|
|
case 2*360: // 5.25", 2, 9, 40 - 360K
|
|
|
|
media_byte = 0xfd;
|
|
|
|
break;
|
|
|
|
case 2*720: // 3.5", 2, 9, 80 - 720K
|
|
|
|
case 2*1200: // 5.25", 2, 15, 80 - 1200K
|
|
|
|
media_byte = 0xf9;
|
|
|
|
break;
|
|
|
|
default: // anything else
|
|
|
|
if (not_floppy)
|
|
|
|
goto set_cluster_size;
|
|
|
|
case 2*1440: // 3.5", 2, 18, 80 - 1440K
|
|
|
|
case 2*2880: // 3.5", 2, 36, 80 - 2880K
|
|
|
|
media_byte = 0xf0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// not floppy, but size matches floppy exactly.
|
|
|
|
// perhaps it is a floppy image.
|
|
|
|
// we already set media_byte as if it is a floppy,
|
|
|
|
// now set sect_per_track and heads.
|
|
|
|
heads = 2;
|
|
|
|
sect_per_track = (unsigned)volume_size_sect / 160;
|
|
|
|
if (sect_per_track < 9)
|
|
|
|
sect_per_track = 9;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Calculate number of clusters, sectors/cluster, sectors/FAT
|
|
|
|
// (an initial guess for sect_per_clust should already be set)
|
|
|
|
//
|
2009-03-28 17:47:20 +05:30
|
|
|
// "mkdosfs -v -F 32 image5k 5" is the minimum:
|
|
|
|
// 2 sectors for FATs and 2 data sectors
|
|
|
|
if ((off_t)(volume_size_sect - reserved_sect) < 4)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg_and_die("the image is too small for FAT32");
|
2009-03-28 07:43:01 +05:30
|
|
|
sect_per_fat = 1;
|
|
|
|
while (1) {
|
|
|
|
while (1) {
|
2009-03-28 17:47:20 +05:30
|
|
|
int spf_adj;
|
2009-11-03 18:42:04 +05:30
|
|
|
uoff_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
|
2009-03-28 07:43:01 +05:30
|
|
|
// tcl may be > MAX_CLUST_32 here, but it may be
|
|
|
|
// because sect_per_fat is underestimated,
|
|
|
|
// and with increased sect_per_fat it still may become
|
|
|
|
// <= MAX_CLUST_32. Therefore, we do not check
|
|
|
|
// against MAX_CLUST_32, but against a bigger const:
|
2009-11-03 18:42:04 +05:30
|
|
|
if (tcl > 0x80ffffff)
|
2009-03-28 07:43:01 +05:30
|
|
|
goto next;
|
|
|
|
total_clust = tcl; // fits in uint32_t
|
2009-11-03 18:42:04 +05:30
|
|
|
// Every cluster needs 4 bytes in FAT. +2 entries since
|
|
|
|
// FAT has space for non-existent clusters 0 and 1.
|
|
|
|
// Let's see how many sectors that needs.
|
|
|
|
//May overflow at "*4":
|
|
|
|
//spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat;
|
|
|
|
//Same in the more obscure, non-overflowing form:
|
|
|
|
spf_adj = ((total_clust+2) + (bytes_per_sect/4)-1) / (bytes_per_sect/4) - sect_per_fat;
|
2009-03-28 17:47:20 +05:30
|
|
|
#if 0
|
|
|
|
bb_error_msg("sect_per_clust:%u sect_per_fat:%u total_clust:%u",
|
|
|
|
sect_per_clust, sect_per_fat, (int)tcl);
|
|
|
|
bb_error_msg("adjust to sect_per_fat:%d", spf_adj);
|
|
|
|
#endif
|
|
|
|
if (spf_adj <= 0) {
|
2009-03-28 07:43:01 +05:30
|
|
|
// do not need to adjust sect_per_fat.
|
|
|
|
// so, was total_clust too big after all?
|
|
|
|
if (total_clust <= MAX_CLUST_32)
|
|
|
|
goto found_total_clust; // no
|
|
|
|
// yes, total_clust is _a bit_ too big
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
// adjust sect_per_fat, go back and recalc total_clust
|
2009-03-28 17:47:20 +05:30
|
|
|
// (note: just "sect_per_fat += spf_adj" isn't ok)
|
|
|
|
sect_per_fat += ((unsigned)spf_adj / 2) | 1;
|
2009-03-28 07:43:01 +05:30
|
|
|
}
|
|
|
|
next:
|
|
|
|
if (sect_per_clust == 128)
|
libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().
This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.
Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.
This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.
The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):
Arm: -92 bytes
MIPS: -52 bytes
PPC: -1836 bytes
x86_64: -938 bytes
Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.
Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
|
|
|
bb_simple_error_msg_and_die("can't make FAT32 with >128 sectors/cluster");
|
2009-03-28 07:43:01 +05:30
|
|
|
sect_per_clust *= 2;
|
|
|
|
sect_per_fat = (sect_per_fat / 2) | 1;
|
|
|
|
}
|
|
|
|
found_total_clust:
|
|
|
|
|
|
|
|
//
|
|
|
|
// Print info
|
|
|
|
//
|
|
|
|
if (opts & OPT_v) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Device '%s':\n"
|
|
|
|
"heads:%u, sectors/track:%u, bytes/sector:%u\n"
|
2009-03-28 17:47:20 +05:30
|
|
|
"media descriptor:%02x\n"
|
2009-03-28 07:43:01 +05:30
|
|
|
"total sectors:%"OFF_FMT"u, clusters:%u, sectors/cluster:%u\n"
|
|
|
|
"FATs:2, sectors/FAT:%u\n"
|
|
|
|
"volumeID:%08x, label:'%s'\n",
|
|
|
|
device_name,
|
|
|
|
heads, sect_per_track, bytes_per_sect,
|
|
|
|
(int)media_byte,
|
|
|
|
volume_size_sect, (int)total_clust, (int)sect_per_clust,
|
|
|
|
sect_per_fat,
|
|
|
|
(int)volume_id, volume_label
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Write filesystem image sequentially (no seeking)
|
|
|
|
//
|
|
|
|
{
|
2009-03-28 07:48:49 +05:30
|
|
|
// (a | b) is poor man's max(a, b)
|
2009-03-28 07:43:01 +05:30
|
|
|
unsigned bufsize = reserved_sect;
|
|
|
|
//bufsize |= sect_per_fat; // can be quite large
|
|
|
|
bufsize |= 2; // use this instead
|
|
|
|
bufsize |= sect_per_clust;
|
|
|
|
buf = xzalloc(bufsize * bytes_per_sect);
|
|
|
|
}
|
|
|
|
|
2009-03-28 07:58:58 +05:30
|
|
|
{ // boot and fsinfo sectors, and their copies
|
2009-03-28 07:43:01 +05:30
|
|
|
struct msdos_boot_sector *boot_blk = (void*)buf;
|
|
|
|
struct fat32_fsinfo *info = (void*)(buf + bytes_per_sect);
|
|
|
|
|
2010-06-04 19:25:17 +05:30
|
|
|
strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs");
|
2009-03-28 07:43:01 +05:30
|
|
|
STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect);
|
|
|
|
STORE_LE(boot_blk->sect_per_clust, sect_per_clust);
|
2009-11-07 06:01:14 +05:30
|
|
|
// cast in needed on big endian to suppress a warning
|
|
|
|
STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect);
|
2009-03-28 07:43:01 +05:30
|
|
|
STORE_LE(boot_blk->fats, 2);
|
|
|
|
//STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0
|
|
|
|
if (volume_size_sect <= 0xffff)
|
|
|
|
STORE_LE(boot_blk->volume_size_sect, volume_size_sect);
|
|
|
|
STORE_LE(boot_blk->media_byte, media_byte);
|
2009-03-28 07:48:49 +05:30
|
|
|
// wrong: this would make Linux think that it's fat12/16:
|
2009-03-28 07:43:01 +05:30
|
|
|
//if (sect_per_fat <= 0xffff)
|
|
|
|
// STORE_LE(boot_blk->sect_per_fat, sect_per_fat);
|
2009-03-28 07:48:49 +05:30
|
|
|
// works:
|
2009-03-28 07:43:01 +05:30
|
|
|
//STORE_LE(boot_blk->sect_per_fat, 0);
|
|
|
|
STORE_LE(boot_blk->sect_per_track, sect_per_track);
|
|
|
|
STORE_LE(boot_blk->heads, heads);
|
|
|
|
//STORE_LE(boot_blk->hidden, 0);
|
|
|
|
STORE_LE(boot_blk->fat32_volume_size_sect, volume_size_sect);
|
|
|
|
STORE_LE(boot_blk->fat32_sect_per_fat, sect_per_fat);
|
|
|
|
//STORE_LE(boot_blk->fat32_flags, 0);
|
|
|
|
//STORE_LE(boot_blk->fat32_version[2], 0,0);
|
|
|
|
STORE_LE(boot_blk->fat32_root_cluster, 2);
|
|
|
|
STORE_LE(boot_blk->fat32_info_sector, info_sector_number);
|
|
|
|
STORE_LE(boot_blk->fat32_backup_boot, backup_boot_sector);
|
|
|
|
//STORE_LE(boot_blk->reserved2[3], 0,0,0);
|
|
|
|
STORE_LE(boot_blk->vi.ext_boot_sign, 0x29);
|
|
|
|
STORE_LE(boot_blk->vi.volume_id32, volume_id);
|
2018-06-26 19:05:17 +05:30
|
|
|
memcpy(boot_blk->vi.fs_type, "FAT32 ", sizeof(boot_blk->vi.fs_type));
|
2009-03-28 07:43:01 +05:30
|
|
|
strncpy(boot_blk->vi.volume_label, volume_label, sizeof(boot_blk->vi.volume_label));
|
|
|
|
memcpy(boot_blk->boot_code, boot_code, sizeof(boot_code));
|
|
|
|
STORE_LE(boot_blk->boot_sign, BOOT_SIGN);
|
|
|
|
|
|
|
|
STORE_LE(info->signature1, FAT_FSINFO_SIG1);
|
|
|
|
STORE_LE(info->signature2, FAT_FSINFO_SIG2);
|
|
|
|
// we've allocated cluster 2 for the root dir
|
|
|
|
STORE_LE(info->free_clusters, (total_clust - 1));
|
|
|
|
STORE_LE(info->next_cluster, 2);
|
|
|
|
STORE_LE(info->boot_sign, BOOT_SIGN);
|
|
|
|
|
|
|
|
// 1st copy
|
|
|
|
xwrite(dev, buf, bytes_per_sect * backup_boot_sector);
|
|
|
|
// 2nd copy and possibly zero sectors
|
|
|
|
xwrite(dev, buf, bytes_per_sect * (reserved_sect - backup_boot_sector));
|
|
|
|
}
|
|
|
|
|
|
|
|
{ // file allocation tables
|
|
|
|
unsigned i,j;
|
|
|
|
unsigned char *fat = (void*)buf;
|
|
|
|
|
|
|
|
memset(buf, 0, bytes_per_sect * 2);
|
|
|
|
// initial FAT entries
|
2009-03-28 07:58:58 +05:30
|
|
|
MARK_CLUSTER(0, 0x0fffff00 | media_byte);
|
|
|
|
MARK_CLUSTER(1, 0xffffffff);
|
2009-03-28 07:43:01 +05:30
|
|
|
// mark cluster 2 as EOF (used for root dir)
|
2009-03-28 07:58:58 +05:30
|
|
|
MARK_CLUSTER(2, EOF_FAT32);
|
2009-03-28 07:43:01 +05:30
|
|
|
for (i = 0; i < NUM_FATS; i++) {
|
|
|
|
xwrite(dev, buf, bytes_per_sect);
|
|
|
|
for (j = 1; j < sect_per_fat; j++)
|
|
|
|
xwrite(dev, buf + bytes_per_sect, bytes_per_sect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// root directory
|
|
|
|
// empty directory is just a set of zero bytes
|
|
|
|
memset(buf, 0, sect_per_clust * bytes_per_sect);
|
|
|
|
if (volume_label[0]) {
|
|
|
|
// create dir entry for volume_label
|
|
|
|
struct msdos_dir_entry *de;
|
|
|
|
#if 0
|
2010-01-09 23:40:49 +05:30
|
|
|
struct tm tm_time;
|
2009-03-28 07:43:01 +05:30
|
|
|
uint16_t t, d;
|
|
|
|
#endif
|
|
|
|
de = (void*)buf;
|
|
|
|
strncpy(de->name, volume_label, sizeof(de->name));
|
|
|
|
STORE_LE(de->attr, ATTR_VOLUME);
|
|
|
|
#if 0
|
2010-01-09 23:40:49 +05:30
|
|
|
localtime_r(&create_time, &tm_time);
|
|
|
|
t = (tm_time.tm_sec >> 1) + (tm_time.tm_min << 5) + (tm_time.tm_hour << 11);
|
|
|
|
d = tm_time.tm_mday + ((tm_time.tm_mon+1) << 5) + ((tm_time.tm_year-80) << 9);
|
2009-03-28 07:43:01 +05:30
|
|
|
STORE_LE(de->time, t);
|
|
|
|
STORE_LE(de->date, d);
|
|
|
|
//STORE_LE(de->ctime_cs, 0);
|
|
|
|
de->ctime = de->time;
|
|
|
|
de->cdate = de->date;
|
|
|
|
de->adate = de->date;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
xwrite(dev, buf, sect_per_clust * bytes_per_sect);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (opts & OPT_c) {
|
2009-03-28 08:52:08 +05:30
|
|
|
uoff_t volume_size_blocks;
|
2009-03-28 07:43:01 +05:30
|
|
|
unsigned start_data_sector;
|
|
|
|
unsigned start_data_block;
|
|
|
|
unsigned badblocks = 0;
|
|
|
|
int try, got;
|
|
|
|
off_t currently_testing;
|
|
|
|
char *blkbuf = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS);
|
|
|
|
|
2009-03-28 08:52:08 +05:30
|
|
|
volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS);
|
2009-03-28 07:43:01 +05:30
|
|
|
// N.B. the two following vars are in hard sectors, i.e. SECTOR_SIZE byte sectors!
|
|
|
|
start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
|
|
|
|
start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
|
|
|
|
|
2016-03-30 19:53:10 +05:30
|
|
|
bb_error_msg("searching for bad blocks");
|
2009-03-28 07:43:01 +05:30
|
|
|
currently_testing = 0;
|
|
|
|
try = TEST_BUFFER_BLOCKS;
|
|
|
|
while (currently_testing < volume_size_blocks) {
|
|
|
|
if (currently_testing + try > volume_size_blocks)
|
|
|
|
try = volume_size_blocks - currently_testing;
|
|
|
|
// perform a test on a block. return the number of blocks
|
|
|
|
// that could be read successfully.
|
|
|
|
// seek to the correct location
|
|
|
|
xlseek(dev, currently_testing * BLOCK_SIZE, SEEK_SET);
|
|
|
|
// try reading
|
|
|
|
got = read(dev, blkbuf, try * BLOCK_SIZE);
|
|
|
|
if (got < 0)
|
|
|
|
got = 0;
|
|
|
|
if (got & (BLOCK_SIZE - 1))
|
2009-04-29 17:32:57 +05:30
|
|
|
bb_error_msg("unexpected values in do_check: probably bugs");
|
2009-03-28 07:43:01 +05:30
|
|
|
got /= BLOCK_SIZE;
|
|
|
|
currently_testing += got;
|
|
|
|
if (got == try) {
|
|
|
|
try = TEST_BUFFER_BLOCKS;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
try = 1;
|
|
|
|
if (currently_testing < start_data_block)
|
|
|
|
bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
|
|
|
|
|
|
|
|
// mark all of the sectors in the block as bad
|
|
|
|
for (i = 0; i < SECTORS_PER_BLOCK; i++) {
|
|
|
|
int cluster = (currently_testing * SECTORS_PER_BLOCK + i - start_data_sector) / (int) (sect_per_clust) / (bytes_per_sect / SECTOR_SIZE);
|
|
|
|
if (cluster < 0)
|
2009-04-29 17:32:57 +05:30
|
|
|
bb_error_msg_and_die("invalid cluster number in mark_sector: probably bug!");
|
2009-03-28 07:58:58 +05:30
|
|
|
MARK_CLUSTER(cluster, BAD_FAT32);
|
2009-03-28 07:43:01 +05:30
|
|
|
}
|
|
|
|
badblocks++;
|
|
|
|
currently_testing++;
|
|
|
|
}
|
|
|
|
free(blkbuf);
|
|
|
|
if (badblocks)
|
2016-03-30 19:53:10 +05:30
|
|
|
bb_error_msg("%d bad block(s)", badblocks);
|
2009-03-28 07:43:01 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// cleanup
|
|
|
|
if (ENABLE_FEATURE_CLEAN_UP) {
|
|
|
|
free(buf);
|
|
|
|
close(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|