/*++ 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 #include #include #include #include #include #include #include #include #include #include #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; }