How to properly write to a tape drive in linux with C/++?

  alignment, c++, io, linux, sequential

I’m currently trying to write to a LTO tape device via C++. My problem is, that subsequent write requests will result in "Invalid argument" errors, while the first call seemingly works just fine.

I have set up the tape drive to use a fixed block size of 64k (Which means, that it can only be written to in chunks of 64k or 64k multiples at a time).
When now trying to write via fwrite() to it, the first call will be successful (or at least return that the requested amount of entries where written), but subsequent calls (with the same parameters) will result in no data being written (fwrite returns 0) and an error of "Invalid argument".
Following is a small sample application thrown together to replicate the issue.

#include <iostream>
#include <unistd.h>

#define BLOCK_SIZE (64 * 1024)
#define DATA_BLOCKS 32
#define byte unsigned char

int main(int argc, char *argv[]) {
    if (argc <= 1) {
        std::cout << "Missing first parameter: Target filen";
        return 1;
    }

    // Create file handle to access tape device in RW mode
    FILE* handle;
    const char* targetFile = argv[1];
    if (access(targetFile, R_OK | W_OK) == 0) {
        handle = fopen(targetFile, "r+");
        printf("Create handle OK: %sn", targetFile);
    } else {
        printf("Could not access %s: Missing rad or write permissionn", targetFile);
        return 1;
    }

    // Create an byte array with data for DATA_BLOCKS blocks
    byte *data = new byte[BLOCK_SIZE * DATA_BLOCKS];

    // Initialize with some data
    for (int i = 0; i < BLOCK_SIZE * DATA_BLOCKS; i++) {
        data[i] = '5';
    }

    // Write data in BLOCK_SIZE chunks, blocksToWrite times per fwrite() call, in numberOfWriteCalls fwrite() calls
    size_t blocksToWrite = 4;
    int numberOfWriteCalls = 5;

    size_t written;
    for (int i = 0; i < numberOfWriteCalls; i++) {
        written = fwrite(data, BLOCK_SIZE, blocksToWrite, handle);
        printf("Round %d: Wrote %d entries of expected %d entriesn", i+1, (int) written, (int) blocksToWrite);

        // Check if there was an error and if so, print error info to stderr
        if (ferror(handle)) {
            printf("Error while writing:n");
            perror("");
            clearerr(handle);
        }

        /* Start modification: Added flushing, as pointed out by user7860670 */
        fflush(handle);

        // Check if there was an error and if so, print error info to stderr
        if (ferror(handle)) {
            printf("Error while flushing:n");
            perror("");
            clearerr(handle);
        }
        /* End modification: Added flushing, as pointed out by user7860670 */
    }

    delete[] data;

    // Close file handle
    fclose(handle);

    return 0;
}

This code calls the fwrite function 5 times, each time trying to write 4 blocks of 64k data.
This works fine on normal files, but returns the following output on all of my tape devices:

Create handle OK: /dev/nst0
Round 1: Wrote 4 entries of expected 4 entries
Round 2: Wrote 0 entries of expected 4 entries
Invalid argument
Round 3: Wrote 0 entries of expected 4 entries
Invalid argument
Round 4: Wrote 0 entries of expected 4 entries
Invalid argument
Round 5: Wrote 0 entries of expected 4 entries
Invalid argument

As can be seen, the first call reacts as expected: fwrite return that 4 blocks of data have been written. But all subsequent calls, even while using the same parameters, will return 0 written blocks and an error of "Invalid argument".
Is there any "special sauce" when trying to write to tape files that I’m not aware of, or am I maybe using the fwrite functionality in a wrong way?

Source: Windows Questions C++

LEAVE A COMMENT