0

I'm trying to use the sudo dd if=/dev/sda ibs=1 count=64 skip=446 command to get the partition table information from the master boot record in order to parse it I'm basically trying to read the output to a string in order to parse it, but all I'm getting is the following: � !. What I'm expecting is:

80 01 01 00 83 FE 3F 01 3F 00 00 00 43 7D 00 00 00 00 01 02 83 FE 3F 0D 82 7D 00 00 0C F1 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

My current code looks like this, and is just taken from here: How to execute a command and get output of command within C++ using POSIX?

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

using namespace std;

string exec(const char* cmd) {
    char buffer[128];
    string result = "";
    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!");
    try {
        while (!feof(pipe)) {
            if (fgets(buffer, 128, pipe) != NULL)
                result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

int main() {

    string s = exec("sudo dd if=/dev/sda ibs=1 count=64 skip=446");
    cout << s;

}

Obviously I'm doing something wrong, but I can't figure out the problem. How do I get the proper output into my string?

Community
  • 1
  • 1
PCR
  • 265
  • 1
  • 5
  • 14
  • BTW, you should read [Why is “while ( !feof (file) )” always wrong?](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – molbdnilo Apr 04 '17 at 09:57
  • 1
    If you run the command from the command-line, will the output be what you expect? I seriously doubt it, because the command will just dump the raw binary data. And raw binary data can't be handled like text. – Some programmer dude Apr 04 '17 at 09:57
  • Also, why are you putting the reading loop in a `try-catch`? The only thing that could cause an exception is if you run out of memory while appending to the string. And even so, the program termination would close the pipe anyway so there's no practical need for it. – Some programmer dude Apr 04 '17 at 10:00
  • Ah thank you. I added `| hexdump -C` and it works fine now – PCR Apr 04 '17 at 10:02
  • Why are you even running `dd` in an external process? You can just `open("/dev/sda"...)` and `seek(446)` and `read(64...)` whatever you want. – Mark Setchell Apr 04 '17 at 10:15

1 Answers1

0
while (!feof(pipe)) {

This is your first bug.

result += buffer;

This is your second bug. buffer is a char array, which decays to a char * in this context. As you know, a char * in a string context gets typically interpreted as a C-style string that's terminated by a '\0' byte.

You might've noticed that you expect to get a bunch of 00 bytes read. Well, after the char array gets decayed to a char *, everything up to the first 00 byte is going to get appended to your result, rather than the 128 bytes, exactly. And if there were no 00 bytes in those 128 bytes, you'll probably end up getting some random garbage, as an extra bonus, with a small possibility of a crash.

if (fgets(buffer, 128, pipe) != NULL)

This is your third bug. If the read data happens to include a 0A byte, an '\n' character, this is not going to read 128 bytes.

cout << s;

This is your fourth bug. Since the data will (after all the other bugs are fixed) presumably contain binary stuff, your terminal is inlikely to have much success displaying various bytes, especially bytes 00 through 1F.

To fix your code you will need to:

  1. Correctly handle the end-of-file condition.

  2. Correctly read binary data. fgets(), et al, are completely unsuitable for the task. If you insist on using C file structures, your only reasonable option is to use fread().

  3. Correctly assemble a std::string from a blob of binary data. Merely appending a char buffer to it, crossing your fingers, and hoping for the best, will not work. You will most likely need to use the two-argument std::string constructor, that takes a beginning and an ending iterator value as parameters.

  4. Display binary data correctly, instead of just dumping the entire blob to std::cout, just like that. The most common approach is a std::hex manipulator, and diligent up-conversion of each char to an int, as an unsigned value.

Community
  • 1
  • 1
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148