dd: support for O_DIRECT i/o

Based on patch by Akash Hadke <hadkeakash4@gmail.com>

function                                             old     new   delta
dd_read                                                -      66     +66
clear_O_DIRECT                                         -      55     +55
write_and_stats                                      102     135     +33
dd_main                                             1578    1601     +23
static.oflag_words                                    19      26      +7
static.iflag_words                                    22      29      +7
packed_usage                                       33665   33668      +3
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 5/0 up/down: 194/0)             Total: 194 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2020-12-14 20:27:57 +01:00
parent c7b858ff8d
commit 2aaacc1453

View File

@ -59,7 +59,7 @@
//usage: "[if=FILE] [of=FILE] [" IF_FEATURE_DD_IBS_OBS("ibs=N obs=N/") "bs=N] [count=N] [skip=N] [seek=N]\n"
//usage: IF_FEATURE_DD_IBS_OBS(
//usage: " [conv=notrunc|noerror|sync|fsync]\n"
//usage: " [iflag=skip_bytes|fullblock] [oflag=seek_bytes|append]"
//usage: " [iflag=skip_bytes|fullblock|direct] [oflag=seek_bytes|append|direct]"
//usage: )
//usage:#define dd_full_usage "\n\n"
//usage: "Copy a file with converting and formatting\n"
@ -82,9 +82,11 @@
//usage: "\n conv=fsync Physically write data out before finishing"
//usage: "\n conv=swab Swap every pair of bytes"
//usage: "\n iflag=skip_bytes skip=N is in bytes"
//usage: "\n iflag=fullblock Read full blocks"
//usage: "\n oflag=seek_bytes seek=N is in bytes"
//usage: "\n oflag=append Open output file in append mode"
//usage: "\n iflag=direct O_DIRECT input"
//usage: "\n oflag=direct O_DIRECT output"
//usage: "\n iflag=fullblock Read full blocks"
//usage: "\n oflag=append Open output in append mode"
//usage: )
//usage: IF_FEATURE_DD_STATUS(
//usage: "\n status=noxfer Suppress rate output"
@ -137,16 +139,18 @@ enum {
FLAG_IFLAG_SHIFT = 5,
FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_FULLBLOCK = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_IDIRECT = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
/* end of input flags */
/* start of output flags */
FLAG_OFLAG_SHIFT = 7,
FLAG_SEEK_BYTES = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_APPEND = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_OFLAG_SHIFT = 8,
FLAG_SEEK_BYTES = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_APPEND = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_ODIRECT = (1 << 10) * ENABLE_FEATURE_DD_IBS_OBS,
/* end of output flags */
FLAG_TWOBUFS = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_COUNT = 1 << 10,
FLAG_STATUS_NONE = 1 << 11,
FLAG_STATUS_NOXFER = 1 << 12,
FLAG_TWOBUFS = (1 << 11) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_COUNT = 1 << 12,
FLAG_STATUS_NONE = 1 << 13,
FLAG_STATUS_NOXFER = 1 << 14,
};
static void dd_output_status(int UNUSED_PARAM cur_signal)
@ -192,12 +196,50 @@ static void dd_output_status(int UNUSED_PARAM cur_signal)
#endif
}
#if ENABLE_FEATURE_DD_IBS_OBS
static int clear_O_DIRECT(int fd)
{
if (errno == EINVAL) {
int fl = fcntl(fd, F_GETFL);
if (fl & O_DIRECT) {
fcntl(fd, F_SETFL, fl & ~O_DIRECT);
return 1;
}
}
return 0;
}
#endif
static ssize_t dd_read(void *ibuf, size_t ibs)
{
ssize_t n;
#if ENABLE_FEATURE_DD_IBS_OBS
read_again:
if (G.flags & FLAG_FULLBLOCK)
n = full_read(ifd, ibuf, ibs);
else
#endif
n = safe_read(ifd, ibuf, ibs);
#if ENABLE_FEATURE_DD_IBS_OBS
if (n < 0 && (G.flags & FLAG_IDIRECT) && clear_O_DIRECT(ifd))
goto read_again;
#endif
return n;
}
static bool write_and_stats(const void *buf, size_t len, size_t obs,
const char *filename)
{
ssize_t n;
IF_FEATURE_DD_IBS_OBS(write_again:)
n = full_write(ofd, buf, len);
#if ENABLE_FEATURE_DD_IBS_OBS
if (n < 0 && (G.flags & FLAG_ODIRECT) && clear_O_DIRECT(ofd))
goto write_again;
#endif
#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
if (n > 0)
G.total_bytes += n;
@ -254,6 +296,14 @@ static int parse_comma_flags(char *val, const char *words, const char *error_in)
}
#endif
static void *alloc_buf(size_t size)
{
/* Important for "{i,o}flag=direct" - buffers must be page aligned */
if (size >= bb_getpagesize())
return xmmap_anon(size);
return xmalloc(size);
}
int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int dd_main(int argc UNUSED_PARAM, char **argv)
{
@ -267,9 +317,9 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
static const char conv_words[] ALIGN1 =
"notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
static const char iflag_words[] ALIGN1 =
"skip_bytes\0""fullblock\0";
"skip_bytes\0""fullblock\0""direct\0";
static const char oflag_words[] ALIGN1 =
"seek_bytes\0append\0";
"seek_bytes\0append\0""direct\0";
#endif
#if ENABLE_FEATURE_DD_STATUS
static const char status_words[] ALIGN1 =
@ -310,7 +360,9 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
//swab swap every pair of input bytes: will abort on non-even reads
OP_iflag_skip_bytes,
OP_iflag_fullblock,
OP_iflag_direct,
OP_oflag_seek_bytes,
OP_oflag_direct,
#endif
};
smallint exitcode = EXIT_FAILURE;
@ -426,13 +478,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
#endif
} /* end of "for (argv[i])" */
//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
ibuf = xmalloc(ibs);
ibuf = alloc_buf(ibs);
obuf = ibuf;
#if ENABLE_FEATURE_DD_IBS_OBS
if (ibs != obs) {
G.flags |= FLAG_TWOBUFS;
obuf = xmalloc(obs);
obuf = alloc_buf(obs);
}
#endif
@ -444,7 +495,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
#endif
if (infile) {
xmove_fd(xopen(infile, O_RDONLY), ifd);
int iflag = O_RDONLY;
#if ENABLE_FEATURE_DD_IBS_OBS
if (G.flags & FLAG_IDIRECT)
iflag |= O_DIRECT;
#endif
xmove_fd(xopen(infile, iflag), ifd);
} else {
infile = bb_msg_standard_input;
}
@ -455,7 +511,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
oflag |= O_TRUNC;
if (G.flags & FLAG_APPEND)
oflag |= O_APPEND;
#if ENABLE_FEATURE_DD_IBS_OBS
if (G.flags & FLAG_ODIRECT)
oflag |= O_DIRECT;
#endif
xmove_fd(xopen(outfile, oflag), ofd);
if (seek && !(G.flags & FLAG_NOTRUNC)) {
@ -478,13 +537,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs;
if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) {
do {
ssize_t n;
#if ENABLE_FEATURE_DD_IBS_OBS
if (G.flags & FLAG_FULLBLOCK)
n = full_read(ifd, ibuf, blocksz);
else
#endif
n = safe_read(ifd, ibuf, blocksz);
ssize_t n = dd_read(ibuf, blocksz);
if (n < 0)
goto die_infile;
if (n == 0)
@ -499,13 +552,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
}
while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
ssize_t n;
#if ENABLE_FEATURE_DD_IBS_OBS
if (G.flags & FLAG_FULLBLOCK)
n = full_read(ifd, ibuf, ibs);
else
#endif
n = safe_read(ifd, ibuf, ibs);
ssize_t n = dd_read(ibuf, ibs);
if (n == 0)
break;
if (n < 0) {
@ -600,11 +647,13 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE))
dd_output_status(0);
#if 0 /* can't just free(), they can be mmap()ed */
if (ENABLE_FEATURE_CLEAN_UP) {
free(obuf);
if (G.flags & FLAG_TWOBUFS)
free(ibuf);
}
#endif
return exitcode;
}