Hello Dr. Flores and welcome.
I developed SD Cloner on Pop!_OS 22.04 LTS (Wayland) using a native C compiler to ensure full compatibility and performance on modern Linux systems.
Thank you, but this is a strange point when this is the expectation for a block device cloning tool. There’s a semi-popular Electron (read: web browser) program out there but its being non-native is why it is slow and not recommended on any basis other than its ease-of-use. I mention this because you bring up the fact that your program runs natively repeatedly throughout your post.
You say you developed this program using a native C compiler as well; are there any reasons you would use a non-native C compiler to develop for Linux? I wasn’t aware that was practical on any platform and had thought even on differing systems cross-compilation was the norm.
I know “burning” is reasonable terminology for writing a block device as it is literal when using optical media but I must admit seeing it in the context of SD cards makes me uneasy as they do on occasion halt and catch fire.
My following notes are regarding the GitHub project.
Your README.md lists the target for your program as the PicoCalc rather than Linux. Is this correct?
Your project’s directory layout in the README.txt lists some non-existent files and no correct paths. It also seems to lead into some malformed MarkDown syntax describing installation.
A part that concerns me is this:
**Safety and Reliability Model**
Source is never modified — all mounts are read-only. Block devices only — GUI validates with `stat()` and `lsblk`. Unmount-before-write safeguard on destination. Comprehensive logging for every shell invocation. Explicit failure modes to prevent silent corruption.
**Test Summary**
Absolutely — here’s a **block-style Markdown table** that looks clean and professional on GitHub. This layout keeps borders visible and content aligned, giving it a structured, report-like appearance:
The safety mechanisms described are standard practice, and the conversational tone of the test output implies it was formatted by a LLM. I find this extremely worrying because that lack of deliberate proofreading of your prose is in the description of a program which is juggling filesystems, a task in which even experts make catastrophic mistakes.
Later the README says:
Partition map mirroring (multi-partition shrink) Real-time byte-progress parsing (libparted integration) Hidden non-removable disks Optional write verification via checksum Dark theme + i18n (GTK theming & gettext)
I skimmed the #includes but I couldn’t find libparted. Where are you using this?
Your C code in general seems underdocumented given its context. For example, this function from sdcloner_engine.c:
// List partitions for a disk (e.g. /dev/sdd -> /dev/sdd1, /dev/sdd2).
// Returns a malloc'd array of strings; caller frees each and the array.
static char** list_partitions(const char* disk, int* out_count) {
char cmd[512];
snprintf(cmd, sizeof(cmd),
"lsblk -rno PATH,TYPE '%s' | awk '$2==\"part\"{print $1}'", disk);
char* out = run_cmd_capture(cmd);
if (!out) { *out_count = 0; return NULL; }
int cap = 8, n = 0;
char** arr = malloc(sizeof(char*) * cap);
char* save = NULL;
for (char* line = strtok_r(out, "\n", &save); line; line = strtok_r(NULL, "\n", &save)) {
if (!*line) continue;
if (n == cap) { cap *= 2; arr = realloc(arr, sizeof(char*) * cap); }
arr[n++] = strdup(line);
}
free(out);
*out_count = n;
return arr;
}
In pseudocode, I might describe this as:
- Run
$ xargs lsblk -rno PATH,TYPE | awk '$2==\"part\"{print $1}' with a standard input of disk
- If the command failed, return an error status.
- Allocate enough memory to keep track of 8 strings; store a reference to an allocated copy of each line from the command output. If the number of strings exceeds the allocation, double it.
- Return that array.
Except, because the cmd is 512B, the function will error out in unpredictable ways if disk is longer than a certain length. This is a very reasonable limit as Linux paths can’t be longer than a few hundred bytes anyway, assuming this program will only ever be used on Linux systems where paths can’t be longer than a few hundred bytes, but this limitation or assumption isn’t documented. In fact, this function is so dense yet so long it took me a moment to understand it myself and longer to piece together (with prior knowledge) that it isn’t a segfault hazard at least on Linux.
A better function might be this:
/* List partitions for the given block devices (e.g. /dev/hd0). Returns a
* malloc(3)d double-nul-terminated array of strings.
* Calls cut(1p), grep(1p), and lsblk(1) (util-linux). */
static char *
list_partitions(const char *disk, int *size) {
static char *cmd = "lsblk -rno PATH,TYPE '%s'" /* list subdevices */
" | grep 'part$'" /* filter partitions */
" | cut -d ' ' -f 1"; /* print paths only */
static char *line = NULL;
int line_l; /* without NUL */
char *retval;
*size = 0;
/* write command line */
line_l = snprintf(NULL, 0, cmd, disk); /* gets length */
if ((line = realloc(line, line_l + 1)) == NULL) { return NULL; }
(void)snprintf(line, line_l + 1, cmd, disk);
/* run, clean, serve */
if (!(retval = run_cmd_capture(cmd))) { return NULL; } /* cmd fail */
for (size_t i = 0; retval[i] != '\0'; ++i) /* replace '\n' with '\0' */
{ if (retval[i] == '\n') { ++*size; retval[i] = '\0'; } }
return retval;
}
It works differently (granting a double-nul-terminated string rather than an array of addresses) but can be adapted. It’s in theory significantly more efficient, safer due to documented assumptions, doesn’t use magic numbers, and is commented enough that understanding this single function doesn’t require understanding the rest of the codebase, but I haven’t tested it so it might have a wrinkle.