[dev][ubase][PATCH] fix several problems in dd

From: isabella parakiss <izaberina_AT_gmail.com>
Date: Fri, 01 Jan 2016 11:21:05 +0100

i've been told on #2f30 that my opinion is void until i submit meaningful
patches. this is a rant^Wbreakdown and fix of a random tool in ubase



$ echo abcd | dd bs=1 count=1 skip=1 2>/dev/null # any other dd
b
$ echo abcd | ubase dd bs=1 count=1 skip=1 quiet; echo $?
dd: copy: Success
22

"wtf is that?" you may be wondering, and i'll show you:

 89| /* check if device or regular file */
 90| if (!S_ISREG(st.st_mode)) {
 91| if (S_ISBLK(st.st_mode)) {
 92| if (ioctl(*ifd, BLKGETSIZE64, &ddc->fsize) < 0) {
 93| ddc->saved_errno = errno;
 94| close(*ifd);
 95| return -1;
 96| }
 97| } else {
 98| ddc->fsize = (off_t)-1;
 99| if (ddc->count)
100| ddc->fsize = ddc->count*ddc->bs;
   | // ^^^^^^^^ THIS IS WRONG ^^^^^^^^
101| }
102| }

108| /* skip more bytes than are inside source file? */
109| if (ddc->fsize != (off_t)-1 && ddc->skip >= (uint64_t)ddc->fsize) {
   | // ^^^^^^^^^^^^^^^^^^^^^^^ THIS ENDS UP BEING TRUE ON A FIFO
110| ddc->saved_errno = EINVAL;
111| close(*ifd);
112| return -1;
113| }


####################

even if that problem is fixed, the whole approach to skip relies on this:

124| lseek(*ifd, ddc->skip, SEEK_SET); // NON SEEKABLE FILES EXIST
   | // ^^^^^^^^ this should be SEEK_CUR anyway


unrelated to skip, but the same problem happens in the line below that

125| lseek(*ofd, ddc->seek, SEEK_SET);
   | // ^^^^^^^^ should be SEEK_CUR


####################

it also breaks filenames with spaces like a poorly written shell script

$ echo abcd > 'file with spaces'; ubase dd quiet if='file with spaces'
dd: copy: No such file or directory


261| if (sscanf(argv[i], "if=%1023s", buf) == 1)
   | // ^^^^^^
262| config.in = strdup(buf);
263| else if (sscanf(argv[i], "of=%1023s", buf) == 1)
   | // ^^^^^^
264| config.out = strdup(buf);


####################

then there's the common suckless approach of using /dev/stdin and
/dev/stdout as the default filenames, to be replaced with user provided
ones, if any

 65| if (*ifd = open(ddc->in, fli)) < 0) {
 66| ddc->saved_errno = errno;
 67| return -1;
 68| }

118| if ((*ofd = open(ddc->out, flo, st.st_mode)) < 0) {
120| ddc->saved_errno = errno;
121| close(*ifd);
122| return -1;
123| }

255| config.in = "/dev/stdin";
256| config.out = "/dev/stdout";

obviously THIS CRAP DOESN'T WORK for dd
open() returns a new file descriptor every time, and in case of a regular
file the file pointer's position is set to the beginning of the file

$ echo abcd > file
$ { dd bs=1 count=1; dd bs=1 count=1; } < file 2>/dev/null # any other dd
ab
$ { ubase dd bs=1 count=1; ubase dd bs=1 count=1; } < file 2>/dev/null
aa


####################

and finally weprintf is called without passing the saved errno

291| if (copy(&config) < 0)
292| weprintf("copy:");

$ echo abcd > file; ubase dd bs=1 count=1 skip=1234 quiet < file
dd: copy: Success

saving errno was useful before importing the code in ubase, when a new
process was forked, but of course it's not needed anymore
so yeah that's "legacy cruft" in a suckless project


####################

there may be more but i lost interest


dd is cat, it's the second program one writes in any language
you can't fuck it up *this* much

also, git log dd.c shows that this file was changed 16 times by multiple
authors, who focused on *important* problems such as what to print with
the -h option, estrtoul instead of strtoul, line wrapping, tabs instead
of spaces... instead of actually testing the fucking thing
but, you know, priorities


patch attached, happy new year

---
xoxo iza

Received on Fri Jan 01 2016 - 11:21:05 CET

This archive was generated by hypermail 2.3.0 : Fri Jan 01 2016 - 11:24:34 CET