1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- dd.c
- Abstract:
- This module implements the dd utility.
- Author:
- Evan Green 13-May-2015
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <unistd.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define DD_VERSION_MAJOR 1
- #define DD_VERSION_MINOR 0
- #define DD_USAGE \
- "usage: dd [operands]\n" \
- "The head command copies the contents of one file to another, \n" \
- "potentially block by block, and potentially with conversions.\n" \
- "Specifications are:\n" \
- " bs=bytes -- Read and write the given number of bytes at a time.\n" \
- " cbs=bytes -- Convert the given number of bytes at a time.\n" \
- " conv=list -- Convert the file according to the given \n" \
- " comma-separated list.\n" \
- " count=N -- Copy only the given number of input blocks.\n" \
- " ibs=bytes -- Read the given number of bytes at a time \n" \
- " (512 by default).\n" \
- " if=file -- Use the given file path as an input rather than stdin.\n" \
- " iflag=flags -- Use the given comma-separated flags for the input.\n" \
- " obs=bytes -- Write the given number of bytes at at time.\n" \
- " of=file -- Write to the given output file instead of stdout.\n" \
- " oflag=flags -- Use the given comma-separated flags for the output.\n" \
- " seek=N -- Skip N obs-sized blocks at the start of the output.\n" \
- " skip=N -- Skip N ibs-sized blocks from the beginning of the input.\n" \
- "Values for conv (conversion can be):\n" \
- " block -- Pad newline-terminated records with spaces to cbs-size.\n" \
- " unblock -- Replace trailing spaces in cbs-size records with newlines.\n"\
- " lcase -- Change all characters to lower case.\n" \
- " ucase -- Change all characters to upper case.\n" \
- " sparse -- Try to seek instead of writing the output for NUL input \n" \
- " blocks.\n" \
- " swab -- Swap every two input bytes.\n" \
- " sync -- Pad every input block with zeros out to ibs-size. \n" \
- " If used with block or unblock, pads with spaces rather than zeros.\n" \
- " excl -- Fail if the output file already exists.\n" \
- " nocreat -- Do not create the file.\n" \
- " notrunc -- Do not truncate the file.\n" \
- " noerror - Continue after read errors.\n" \
- "Values for flags:\n" \
- " fullblock -- Accumulate full blocks of input.\n" \
- " nonblock -- Use non-blocking I/O.\n" \
- " noatime -- Do not update access time when opening the file.\n" \
- " noctty -- Do not adopt a file as the controlling terminal.\n" \
- " nofollow -- Do not follow symlinks.\n" \
- " count_bytes -- Treat count=N as a byte count (input only).\n" \
- " skip_bytes -- Treat skip=N as a byte count (input only).\n" \
- " seek_bytes -- Treat seek=N as a byte count (output only).\n" \
- "Sending a SIGUSR1 to dd causes it to print its current I/O statistics\n" \
- "and keep going. Sending a SIGINT causes dd to print its current I/O\n" \
- "statistics and exit.\n" \
- "Options are:\n" \
- " --help -- Show this help text and exit.\n" \
- " --version - Show the application version information and exit.\n"
- #define DD_OPTIONS_STRING ""
- #define DD_DEFAULT_BLOCK_SIZE 512
- #define DD_DEFAULT_IN_OPEN_FLAGS (O_RDONLY)
- #define DD_DEFAULT_OUT_OPEN_FLAGS (O_CREAT | O_TRUNC | O_WRONLY)
- #define DD_DEFAULT_CREATION_MASK 0644
- //
- // Define dd options.
- //
- //
- // Set this option to pad newline-terminated records to conversion block size.
- //
- #define DD_OPTION_BLOCK 0x00000001
- //
- // Set this option to replace trailing spaces in conversion-block sized
- // records with a newline.
- //
- #define DD_OPTION_UNBLOCK 0x00000002
- //
- // Set this option to convert characters to lower case.
- //
- #define DD_OPTION_LOWERCASE 0x00000004
- //
- // Set this option to convert characters to upper case.
- //
- #define DD_OPTION_UPPERCASE 0x00000008
- //
- // Set this option to try and seek rather than output zeroed input blocks.
- //
- #define DD_OPTION_SPARSE 0x00000010
- //
- // Set this option to swap every two bytes of input.
- //
- #define DD_OPTION_SWAB 0x00000020
- //
- // Set this option to pad every input block with zeros out to input block
- // size. Pads with spaces when used with block or unblock.
- //
- #define DD_OPTION_SYNC 0x00000040
- //
- // Set this option to continue even on errors.
- //
- #define DD_OPTION_NO_ERROR 0x00000080
- //
- // Set this option to treat the input count as bytes.
- //
- #define DD_OPTION_COUNT_BYTES 0x00000100
- //
- // Set this option to treat the skip count as bytes.
- //
- #define DD_OPTION_SKIP_BYTES 0x00000200
- //
- // Set this option to tread the seek count as bytes.
- //
- #define DD_OPTION_SEEK_BYTES 0x00000400
- //
- // Set this option to accumulate full input blocks.
- //
- #define DD_OPTION_FULL_BLOCKS 0x00000800
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure stores the context for a DD application instance.
- Members:
- InBlockSize - Stores the input block size.
- OutBlockSize - Stores the output block size.
- ConvertBlockSize - Stores the character conversion size.
- Options - Stores application options. See DD_OPTION_* definitions.
- Count - Stores the number of input bytes to copy.
- OutSkip - Stores the number of bytes to skip on output.
- InSkip - Stores the number of bytes to skip on input.
- InOpenFlags - Stores the input open flags to use.
- OutOpenFlags - Stores the output open flags to use.
- StartTime - Stores the start time for the operation.
- InWhole - Stores the count of whole blocks that have been read in.
- InPartial - Stores the count of partial blocks that have been read in.
- OutWhole - Stores the count of whole blocks that have been written out.
- OutPartial - Stores the count of partial blocks that have been written out.
- BytesComplete - Stores the count of bytes that have been copied.
- Exit - Stores a boolean indicating the application should exit because a
- SIGINT occurred.
- PrintRequest - Stores a boolean indicating that there is a request for
- I/O statistics pending.
- --*/
- typedef struct _DD_CONTEXT {
- UINTN InBlockSize;
- UINTN OutBlockSize;
- UINTN ConvertBlockSize;
- ULONG Options;
- ULONGLONG Count;
- ULONGLONG OutSkip;
- ULONGLONG InSkip;
- INT InOpenFlags;
- INT OutOpenFlags;
- struct timespec StartTime;
- ULONGLONG InWholeBlocks;
- ULONGLONG InPartialBlocks;
- ULONGLONG OutWholeBlocks;
- ULONGLONG OutPartialBlocks;
- ULONGLONG BytesComplete;
- BOOL Exit;
- BOOL PrintRequest;
- } DD_CONTEXT, *PDD_CONTEXT;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- void
- DdSignalHandler (
- int Signal
- );
- VOID
- DdPrintIoStatistics (
- PDD_CONTEXT Context
- );
- INT
- DdParseConversionArguments (
- PDD_CONTEXT Context,
- PSTR Argument
- );
- INT
- DdParseFileArguments (
- PDD_CONTEXT Context,
- PSTR Argument,
- BOOL IsInput
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option DdLongOptions[] = {
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // Store the global for the dd context, used by the signal handlers.
- //
- PDD_CONTEXT DdContext;
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- DdMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the head utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- struct sigaction Action;
- PSTR Argument;
- ULONG ArgumentIndex;
- PUCHAR Buffer;
- UINTN BufferSize;
- UINTN ByteIndex;
- ssize_t BytesComplete;
- UINTN BytesThisRound;
- DD_CONTEXT Context;
- ULONGLONG Count;
- PSTR InPath;
- int Input;
- INT Option;
- struct sigaction OriginalSigint;
- struct sigaction OriginalSigusr1;
- PSTR OutPath;
- int Output;
- INT Status;
- UCHAR Swap;
- INT TotalStatus;
- memset(&Context, 0, sizeof(DD_CONTEXT));
- DdContext = &Context;
- Context.InBlockSize = DD_DEFAULT_BLOCK_SIZE;
- Context.OutBlockSize = DD_DEFAULT_BLOCK_SIZE;
- Context.ConvertBlockSize = 1;
- Context.InOpenFlags = DD_DEFAULT_IN_OPEN_FLAGS;
- Context.OutOpenFlags = DD_DEFAULT_OUT_OPEN_FLAGS;
- InPath = NULL;
- Input = -1;
- OutPath = NULL;
- Output = -1;
- TotalStatus = 0;
- Status = 0;
- //
- // Wire up the signal handlers.
- //
- memset(&Action, 0, sizeof(struct sigaction));
- Action.sa_handler = DdSignalHandler;
- sigaction(SIGINT, &Action, &OriginalSigint);
- sigaction(SIGUSR1, &Action, &OriginalSigusr1);
- //
- // Process the control arguments, which is pretty much just --help and
- // --version.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- DD_OPTIONS_STRING,
- DdLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'V':
- SwPrintVersion(DD_VERSION_MAJOR, DD_VERSION_MINOR);
- return 1;
- case 'h':
- printf(DD_USAGE);
- Status = 1;
- goto MainEnd;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- while (ArgumentIndex < ArgumentCount) {
- Argument = Arguments[ArgumentIndex];
- if (strstr(Argument, "bs=") == Argument) {
- Argument += 3;
- Context.InBlockSize = SwParseFileSize(Argument);
- if (Context.InBlockSize == -1ULL) {
- SwPrintError(0, Argument, "Invalid block size");
- Status = EINVAL;
- goto MainEnd;
- }
- Context.OutBlockSize = Context.InBlockSize;
- } else if (strstr(Argument, "cbs=") == Argument) {
- Argument += 4;
- Context.ConvertBlockSize = SwParseFileSize(Argument);
- if (Context.ConvertBlockSize == -1ULL) {
- SwPrintError(0, Argument, "Invalid block size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if (strstr(Argument, "conv=") == Argument) {
- Argument += 5;
- Status = DdParseConversionArguments(&Context, Argument);
- if (Status != 0) {
- SwPrintError(Status, Argument, "Invalid conversion argument");
- goto MainEnd;
- }
- } else if (strstr(Argument, "count=") == Argument) {
- Argument += 6;
- Context.Count = SwParseFileSize(Argument);
- if (Context.Count == -1ULL) {
- SwPrintError(0, Argument, "Invalid size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if (strstr(Argument, "ibs=") == Argument) {
- Argument += 4;
- Context.InBlockSize = SwParseFileSize(Argument);
- if (Context.InBlockSize == -1ULL) {
- SwPrintError(0, Argument, "Invalid block size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if (strstr(Argument, "if=") == Argument) {
- Argument += 3;
- InPath = Argument;
- } else if (strstr(Argument, "iflag=") == Argument) {
- Argument += 6;
- Status = DdParseFileArguments(&Context, Argument, TRUE);
- if (Status != 0) {
- SwPrintError(Status, Argument, "Invalid file argument");
- goto MainEnd;
- }
- } else if (strstr(Argument, "obs=") == Argument) {
- Argument += 4;
- Context.OutBlockSize = SwParseFileSize(Argument);
- if (Context.OutBlockSize == -1ULL) {
- SwPrintError(0, Argument, "Invalid block size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if (strstr(Argument, "of=") == Argument) {
- Argument += 3;
- OutPath = Argument;
- } else if (strstr(Argument, "oflag=") == Argument) {
- Argument += 6;
- Status = DdParseFileArguments(&Context, Argument, FALSE);
- if (Status != 0) {
- SwPrintError(Status, Argument, "Invalid file argument");
- goto MainEnd;
- }
- } else if (strstr(Argument, "seek=") == Argument) {
- Argument += 5;
- Context.OutSkip = SwParseFileSize(Argument);
- if (Context.OutSkip == -1ULL) {
- SwPrintError(0, Argument, "Invalid size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else if (strstr(Argument, "skip=") == Argument) {
- Argument += 5;
- Context.InSkip = SwParseFileSize(Argument);
- if (Context.InSkip == -1ULL) {
- SwPrintError(0, Argument, "Invalid size");
- Status = EINVAL;
- goto MainEnd;
- }
- } else {
- SwPrintError(0, Argument, "Unrecognized specification");
- Status = EINVAL;
- goto MainEnd;
- }
- ArgumentIndex += 1;
- }
- //
- // Consider implementing block and unblock if the masses are clamoring for
- // it.
- //
- if ((Context.Options & (DD_OPTION_BLOCK | DD_OPTION_UNBLOCK)) != 0) {
- SwPrintError(0, NULL, "Block/unblock modes currently not implemented");
- Status = ENOSYS;
- goto MainEnd;
- }
- //
- // Allocate a buffer.
- //
- BufferSize = Context.InBlockSize;
- if (BufferSize < Context.OutBlockSize) {
- BufferSize = Context.OutBlockSize;
- }
- if (BufferSize > MAX_UINTN) {
- SwPrintError(0, NULL, "Size %lld is too big", BufferSize);
- Status = ERANGE;
- goto MainEnd;
- }
- Buffer = malloc(BufferSize);
- if (Buffer == NULL) {
- SwPrintError(0, NULL, "Allocation failure");
- Status = ENOMEM;
- goto MainEnd;
- }
- //
- // Multiply up the block sizes if needed.
- //
- if ((Context.Options & DD_OPTION_COUNT_BYTES) == 0) {
- Context.Count *= Context.InBlockSize;
- }
- if ((Context.Options & DD_OPTION_SKIP_BYTES) == 0) {
- Context.InSkip *= Context.InBlockSize;
- }
- if ((Context.Options & DD_OPTION_SEEK_BYTES) == 0) {
- Context.OutSkip *= Context.OutBlockSize;
- }
- SwGetMonotonicClock(&(Context.StartTime));
- //
- // Open up the files if specified.
- //
- if (InPath != NULL) {
- Input = open(InPath, Context.InOpenFlags);
- if (Input < 0) {
- Status = errno;
- SwPrintError(Status, InPath, "Cannot open");
- goto MainEnd;
- }
- } else {
- InPath = "standard in";
- Input = STDIN_FILENO;
- }
- if (OutPath != NULL) {
- Output = open(OutPath, Context.OutOpenFlags, DD_DEFAULT_CREATION_MASK);
- if (Output < 0) {
- Status = errno;
- SwPrintError(Status, OutPath, "Cannot open");
- goto MainEnd;
- }
- } else {
- OutPath = "standard out";
- Output = STDOUT_FILENO;
- }
- //
- // Skip over the beginning input and output.
- //
- if (Context.InSkip != 0) {
- if (lseek(Input, Context.InSkip, SEEK_CUR) < 0) {
- Count = Context.InSkip;
- while (Count != 0) {
- BytesThisRound = Context.InBlockSize;
- if (BytesThisRound > Count) {
- BytesThisRound = Count;
- }
- do {
- BytesComplete = read(Input, Buffer, BytesThisRound);
- } while ((BytesComplete < 0) && (errno == EINTR));
- if (BytesComplete < 0) {
- Status = errno;
- SwPrintError(Status, InPath, "Failed to read during skip");
- goto MainEnd;
- }
- if (BytesComplete == 0) {
- break;
- }
- Count -= BytesComplete;
- }
- }
- }
- if (Context.OutSkip != 0) {
- if (lseek(Output, Context.OutSkip, SEEK_CUR) < 0) {
- Status = errno;
- SwPrintError(Status, OutPath, "Failed to read during seek");
- goto MainEnd;
- }
- }
- //
- // Loop processing data.
- //
- while ((Context.Count == 0) || (Context.BytesComplete < Context.Count)) {
- if ((Context.Options & DD_OPTION_SYNC) != 0) {
- if ((Context.Options &
- (DD_OPTION_BLOCK | DD_OPTION_UNBLOCK)) != 0) {
- memset(Buffer, ' ', Context.InBlockSize);
- } else {
- memset(Buffer, 0, Context.InBlockSize);
- }
- }
- //
- // Read a block.
- //
- BytesThisRound = Context.InBlockSize;
- if ((Context.Count != 0) &&
- (Context.Count - Context.BytesComplete < BytesThisRound)) {
- BytesThisRound = Context.Count - Context.BytesComplete;
- }
- do {
- if (Context.PrintRequest != FALSE) {
- Context.PrintRequest = FALSE;
- DdPrintIoStatistics(&Context);
- }
- if (Context.Exit != FALSE) {
- Status = EINTR;
- goto MainEnd;
- }
- BytesComplete = read(Input, Buffer, BytesThisRound);
- } while ((BytesComplete < 0) && (errno == EINTR));
- if (BytesComplete < 0) {
- Status = errno;
- SwPrintError(Status, InPath, "Failed to read");
- if ((Context.Options & DD_OPTION_NO_ERROR) == 0) {
- goto MainEnd;
- }
- DdPrintIoStatistics(&Context);
- //
- // Try to seek past the problem.
- //
- if (lseek(Input, Context.InBlockSize, SEEK_CUR) < 0) {
- Status = errno;
- SwPrintError(Status, InPath, "Also failed to seek");
- }
- if ((Context.Options & DD_OPTION_SYNC) == 0) {
- continue;
- }
- Context.BytesComplete += BytesThisRound;
- Context.InWholeBlocks += 1;
- } else if (BytesComplete == 0) {
- break;
- } else if (BytesComplete == BytesThisRound) {
- Context.InWholeBlocks += 1;
- Context.BytesComplete += BytesComplete;
- } else {
- Context.InPartialBlocks += 1;
- //
- // Sync just acts like the whole block was read.
- //
- if ((Context.Options & DD_OPTION_SYNC) != 0) {
- Context.BytesComplete += BytesThisRound;
- } else {
- Context.BytesComplete += BytesComplete;
- }
- }
- //
- // Perform conversions.
- //
- if ((Context.Options & DD_OPTION_SWAB) != 0) {
- for (ByteIndex = 0;
- ByteIndex + 1 < BytesComplete;
- ByteIndex += 2) {
- Swap = Buffer[ByteIndex];
- Buffer[ByteIndex] = Buffer[ByteIndex + 1];
- Buffer[ByteIndex + 1] = Swap;
- }
- }
- if ((Context.Options & DD_OPTION_LOWERCASE) != 0) {
- for (ByteIndex = 0; ByteIndex < BytesComplete; ByteIndex += 1) {
- Buffer[ByteIndex] = tolower(Buffer[ByteIndex]);
- }
- }
- if ((Context.Options & DD_OPTION_UPPERCASE) != 0) {
- for (ByteIndex = 0; ByteIndex < BytesComplete; ByteIndex += 1) {
- Buffer[ByteIndex] = toupper(Buffer[ByteIndex]);
- }
- }
- //
- // Skip the write if it's sparse and empty.
- //
- if ((Context.Options & DD_OPTION_SPARSE) != 0) {
- for (ByteIndex = 0; ByteIndex < BytesComplete; ByteIndex += 1) {
- if (Buffer[ByteIndex] != 0) {
- break;
- }
- }
- if (ByteIndex == BytesComplete) {
- if (lseek(Output, BytesComplete, SEEK_CUR) >= 0) {
- continue;
- } else {
- Status = errno;
- SwPrintError(Status, OutPath, "Seek error");
- }
- }
- }
- if (Context.PrintRequest != FALSE) {
- Context.PrintRequest = FALSE;
- DdPrintIoStatistics(&Context);
- }
- if (Context.Exit != FALSE) {
- Status = EINTR;
- goto MainEnd;
- }
- //
- // Write the block out.
- //
- BytesThisRound = BytesComplete;
- do {
- if (Context.PrintRequest != FALSE) {
- Context.PrintRequest = FALSE;
- DdPrintIoStatistics(&Context);
- }
- if (Context.Exit != FALSE) {
- Status = EINTR;
- goto MainEnd;
- }
- BytesComplete = write(Output, Buffer, BytesThisRound);
- } while ((BytesComplete < 0) && (errno == EINTR));
- if (BytesComplete < 0) {
- Status = errno;
- SwPrintError(Status, OutPath, "Write error");
- TotalStatus = 1;
- } else {
- if (BytesComplete == Context.OutBlockSize) {
- Context.OutWholeBlocks += 1;
- } else {
- Context.OutPartialBlocks += 1;
- }
- }
- }
- DdPrintIoStatistics(&Context);
- Status = 0;
- MainEnd:
- sigaction(SIGINT, &OriginalSigint, NULL);
- sigaction(SIGUSR1, &OriginalSigusr1, NULL);
- if (Input > STDIN_FILENO) {
- close(Input);
- }
- if (Output > STDOUT_FILENO) {
- close(Output);
- }
- DdContext = NULL;
- if ((Status != 0) && (TotalStatus == 0)) {
- TotalStatus = Status;
- }
- return TotalStatus;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- void
- DdSignalHandler (
- int Signal
- )
- /*++
- Routine Description:
- This routine handles a SIGINT or SIGUSR1 signal while running the DD
- command.
- Arguments:
- Signal - Supplies the signal number that fired.
- Return Value:
- None.
- --*/
- {
- PDD_CONTEXT Context;
- Context = DdContext;
- if (Context == NULL) {
- fprintf(stderr, "dd: Bad signal timing\n");
- return;
- }
- assert((Signal == SIGINT) || (Signal == SIGUSR1));
- Context->PrintRequest = TRUE;
- if (Signal == SIGINT) {
- Context->Exit = TRUE;
- }
- return;
- }
- VOID
- DdPrintIoStatistics (
- PDD_CONTEXT Context
- )
- /*++
- Routine Description:
- This routine prints I/O statistics for the dd utility.
- Arguments:
- Context - Supplies a pointer to the application context.
- Return Value:
- None.
- --*/
- {
- double Rate;
- double Seconds;
- struct timespec Time;
- PSTR Unit;
- SwGetMonotonicClock(&Time);
- fprintf(stderr,
- "%llu+%llu records in\n%llu+%llu records out\n",
- Context->InWholeBlocks,
- Context->InPartialBlocks,
- Context->OutWholeBlocks,
- Context->OutPartialBlocks);
- Unit = "B";
- Seconds = Time.tv_sec - Context->StartTime.tv_sec;
- if (Seconds < 3600 * 24) {
- Seconds += (Time.tv_nsec - Context->StartTime.tv_nsec) /
- 1000000000.0;
- }
- Rate = (double)(Context->BytesComplete) / Seconds;
- if (Rate > 1024) {
- Rate /= 1024.0;
- Unit = "kB";
- if (Rate > 1024) {
- Rate /= 1024.0;
- Unit = "MB";
- if (Rate > 1024) {
- Rate /= 1024.0;
- Unit = "GB";
- }
- }
- }
- fprintf(stderr, "%f seconds, %f%s/s\n", Seconds, Rate, Unit);
- return;
- }
- INT
- DdParseConversionArguments (
- PDD_CONTEXT Context,
- PSTR Argument
- )
- /*++
- Routine Description:
- This routine processes the conv= comma-separated arguments for the dd
- utility.
- Arguments:
- Context - Supplies a pointer to the application context.
- Argument - Supplies the comma-separated conversion list.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- while (*Argument != '\0') {
- if (strstr(Argument, "ascii") == Argument) {
- SwPrintError(0, NULL, "Not supported");
- return EINVAL;
- } else if (strstr(Argument, "ebcdic") == Argument) {
- SwPrintError(0, NULL, "Not supported");
- return EINVAL;
- } else if (strstr(Argument, "ibm") == Argument) {
- SwPrintError(0, NULL, "Not supported");
- return EINVAL;
- } else if (strstr(Argument, "block") == Argument) {
- Argument += 5;
- Context->Options |= DD_OPTION_BLOCK;
- Context->Options &= ~DD_OPTION_UNBLOCK;
- } else if (strstr(Argument, "unblock") == Argument) {
- Argument += 7;
- Context->Options |= DD_OPTION_UNBLOCK;
- Context->Options &= ~DD_OPTION_BLOCK;
- } else if (strstr(Argument, "lcase") == Argument) {
- Argument += 5;
- Context->Options |= DD_OPTION_LOWERCASE;
- } else if (strstr(Argument, "ucase") == Argument) {
- Argument += 5;
- Context->Options |= DD_OPTION_UPPERCASE;
- } else if (strstr(Argument, "sparse") == Argument) {
- Argument += 6;
- Context->Options |= DD_OPTION_SPARSE;
- } else if (strstr(Argument, "swab") == Argument) {
- Argument += 4;
- Context->Options |= DD_OPTION_SWAB;
- } else if (strstr(Argument, "sync") == Argument) {
- Argument += 4;
- Context->Options |= DD_OPTION_SYNC;
- } else if (strstr(Argument, "excl") == Argument) {
- Argument += 4;
- Context->OutOpenFlags |= O_EXCL;
- } else if (strstr(Argument, "nocreat") == Argument) {
- Argument += 7;
- Context->OutOpenFlags &= ~O_CREAT;
- } else if (strstr(Argument, "notrunc") == Argument) {
- Argument += 7;
- Context->OutOpenFlags &= ~O_TRUNC;
- } else if (strstr(Argument, "noerror") == Argument) {
- Argument += 7;
- Context->Options |= DD_OPTION_NO_ERROR;
- } else {
- SwPrintError(0, Argument, "Unknown option");
- return EINVAL;
- }
- if (*Argument != '\0') {
- if (*Argument != ',') {
- return EINVAL;
- }
- Argument += 1;
- }
- }
- if (((Context->Options & DD_OPTION_UPPERCASE) != 0) &&
- ((Context->Options & DD_OPTION_LOWERCASE) != 0)) {
- SwPrintError(0, NULL, "Cannot combine lowercase and uppercase");
- return EINVAL;
- }
- return 0;
- }
- INT
- DdParseFileArguments (
- PDD_CONTEXT Context,
- PSTR Argument,
- BOOL IsInput
- )
- /*++
- Routine Description:
- This routine processes the iflag= and oflag= comma-separated arguments for
- the dd utility.
- Arguments:
- Context - Supplies a pointer to the application context.
- Argument - Supplies the comma-separated flag list.
- IsInput - Supplies a boolean indicating whether this is the input flags
- being parsed (TRUE) or the output flags (FALSE).
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- INT NewFlags;
- NewFlags = 0;
- while (*Argument != '\0') {
- if (strstr(Argument, "append") == Argument) {
- Argument += 6;
- NewFlags |= O_APPEND;
- } else if (strstr(Argument, "directory") == Argument) {
- Argument += 9;
- NewFlags |= O_DIRECTORY;
- } else if (strstr(Argument, "dsync") == Argument) {
- Argument += 5;
- NewFlags |= O_DSYNC;
- } else if (strstr(Argument, "sync") == Argument) {
- Argument += 4;
- NewFlags |= O_SYNC;
- } else if (strstr(Argument, "nonblock") == Argument) {
- Argument += 8;
- NewFlags |= O_NONBLOCK;
- } else if (strstr(Argument, "noatime") == Argument) {
- Argument += 7;
- NewFlags |= O_NOATIME;
- } else if (strstr(Argument, "noctty") == Argument) {
- Argument += 6;
- NewFlags |= O_NOCTTY;
- } else if (strstr(Argument, "nofollow") == Argument) {
- Argument += 8;
- NewFlags |= O_NOFOLLOW;
- } else {
- //
- // Handle input-specific flags.
- //
- if (IsInput != FALSE) {
- if (strstr(Argument, "fullblock") == Argument) {
- Argument += 9;
- Context->Options |= DD_OPTION_FULL_BLOCKS;
- } else if (strstr(Argument, "count_bytes") == Argument) {
- Argument += 11;
- Context->Options |= DD_OPTION_COUNT_BYTES;
- } else if (strstr(Argument, "skip_bytes") == Argument) {
- Argument += 10;
- Context->Options |= DD_OPTION_SKIP_BYTES;
- } else {
- SwPrintError(0, Argument, "Unknown option");
- return EINVAL;
- }
- //
- // Handle output-specific flags.
- //
- } else {
- if (strstr(Argument, "seek_bytes") == Argument) {
- Argument += 10;
- Context->Options |= DD_OPTION_SEEK_BYTES;
- } else {
- SwPrintError(0, Argument, "Unknown option");
- return EINVAL;
- }
- }
- }
- if (*Argument != '\0') {
- if (*Argument != ',') {
- return EINVAL;
- }
- Argument += 1;
- }
- }
- if (IsInput != FALSE) {
- Context->InOpenFlags |= NewFlags;
- } else {
- Context->OutOpenFlags |= NewFlags;
- }
- return 0;
- }
|