- // qnx6label.c -- read/set the volume labels of qnx6fs partitions by Pierre-Marie Baty <pm@pmbaty.com> 
-   
- #include <stdio.h> 
- #include <stdlib.h> 
- #include <stdint.h> 
- #include <stdbool.h> 
- #include <string.h> 
- #include <errno.h> 
- #include <ctype.h> 
-   
-   
- #define QNX6FS_SUPERBLOCK_MAGIC 0x68191122 
-   
-   
- static uint32_t crc32_be (uint32_t crc, const uint8_t *p, size_t len) 
- { 
-         // big endian CRC32 function to recompute a qnx6fs superblock checksum 
-   
-         const uint32_t CRCPOLY_BE = 0x04c11db7; 
-         size_t bit_index; 
-   
-         while (len--) 
-         { 
-                 crc ^= *p++ << 24; 
-                 for (bit_index = 0; bit_index < 8; bit_index++) 
-                         crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); 
-         } 
-   
-         return (crc); 
- } 
-   
-   
- int main (int argc, char **argv) 
- { 
-         // program entrypoint 
-   
-         // references: 
-         // https://docs.kernel.org/filesystems/qnx6.html 
-         // https://github.com/torvalds/linux/blob/master/fs/qnx6/qnx6.h 
-         // https://github.com/torvalds/linux/blob/master/include/linux/qnx6_fs.h 
-         // https://github.com/torvalds/linux/blob/master/fs/qnx6/inode.c 
-   
-         typedef struct __attribute__((packed)) qnx6_rootnode_s 
-         { 
-                 uint64_t size; 
-                 uint32_t ptr[16]; // 16 block pointers in sbl/inode 
-                 uint8_t levels; 
-                 uint8_t mode; 
-                 uint8_t spare[6]; 
-         } qnx6_rootnode_t; 
-         typedef struct __attribute__((packed)) qnx6_superblock_s 
-         { 
-                 uint32_t sb_magic; // offset 0 -- "\x22\x11\x19\x68" or 0x68191122 
-                 uint32_t sb_checksum; // offset 4 
-                 uint64_t sb_serial; // offset 8 
-                 uint32_t sb_ctime; // offset 16 -- time the fs was created 
-                 uint32_t sb_atime; // offset 20 -- last access time 
-                 uint32_t sb_flags; // offset 24 
-                 uint16_t sb_version1; // offset 28 -- filesystem version info 
-                 uint16_t sb_version2; // offset 30 -- filesystem version info 
-                 uint8_t sb_volumeid[16]; // offset 32 
-                 uint32_t sb_blocksize; // offset 48 
-                 uint32_t sb_num_inodes; // offset 52 
-                 uint32_t sb_free_inodes; // offset 56 
-                 uint32_t sb_num_blocks; // offset 60 
-                 uint32_t sb_free_blocks; // offset 64 
-                 uint32_t sb_allocgroup; // offset 68 
-                 qnx6_rootnode_t rootnode_inode; // offset 72 
-                 qnx6_rootnode_t rootnode_bitmap; // offset 152 
-                 qnx6_rootnode_t rootnode_longfile; // offset 232 
-                 qnx6_rootnode_t rootnode_unknown; // offset 312 
-         } qnx6_superblock_t; // size 392 
-         typedef struct qnx6_superblock_info_s 
-         { 
-                 bool is_mandatory; 
-                 size_t offset; 
-                 uint8_t bytes[512]; 
-                 qnx6_superblock_t *sb; // points at beginning of bytes array 
-         } qnx6_superblock_info_t; 
-   
-         qnx6_superblock_info_t qnx6_superblocks[4] =  
-         { 
-                 { true,  0x2000,   "", (qnx6_superblock_t *) qnx6_superblocks[0].bytes }, // mandatory first superblock 
-                 { false, 0x2e00,   "", (qnx6_superblock_t *) qnx6_superblocks[1].bytes }, 
-                 { true,  SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[2].bytes }, // mandatory last superblock 
-                 { false, SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[3].bytes }, 
-         }; 
-         qnx6_superblock_t *latest_superblock; 
-         const char *dev_pathname; 
-         size_t superblock_index; 
-         size_t label_offset; 
-         size_t char_index; 
-         size_t end_pos; 
-         uint8_t *p; 
-         char *new_label; 
-         char label[17]; 
-         FILE *fp; 
-   
-         // consistency checks 
-         if ((argc < 2) || (*(argv[1]) == '-')) 
-         { 
-                 fprintf (- stderr , "usage: qnx6label <device> [new-label]\n");
 
-         } 
-   
-         dev_pathname = argv[1]; // read arguments 
-         new_label = (argc > 2 ? argv[2] : NULL); 
-   
-         // open and read all mandatory superblocks of the passed device pathname 
-         if ((- fp  = fopen (- dev_pathname , "r+b")) ==-  NULL )
 
-         { 
-                 fprintf (- stderr , "error: can't open %s: %s\n",-  dev_pathname , strerror (- errno ));
 
-         } 
-         if (end_pos < 0x4000) 
-         { 
-                 fprintf (- stderr , "error: %s too small to contain a qnx6 filesystem\n",-  dev_pathname );
 
-         } 
-         qnx6_superblocks[2].offset = (end_pos & ~0xfff) - 0x1000; // compute mandatory last superblock offset 
-         qnx6_superblocks[3].offset = (end_pos & ~0xfff) - 0x1000 + 0xe00; // compute optional last superblock offset 
-         latest_superblock = NULL; 
-         for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++) 
-         { 
-                 // seek at the right offset and read the (supposed) qnx6fs superblock data 
-                 if ((fseek (- fp ,-  qnx6_superblocks [- superblock_index ]- . offset,-  SEEK_SET ) != 0)
 
-                     || (fread (- qnx6_superblocks [- superblock_index ]- . bytes, sizeof (- qnx6_superblocks [- superblock_index ]- . bytes), 1,-  fp ) != 1))
 
-                 { 
-                         fprintf (- stderr , "error: can't read superblock #%zd of %s at 0x%zx: %s\n",-  superblock_index ,-  dev_pathname ,-  qnx6_superblocks [- superblock_index ]- . offset, strerror (- errno ));
 
-                 } 
-   
-                 // validate that all mandatory superblocks are present and keep track of the newest one 
-                 if (qnx6_superblocks[superblock_index].sb->sb_magic == QNX6FS_SUPERBLOCK_MAGIC) 
-                 { 
-                         if ((latest_superblock == NULL) || (qnx6_superblocks[superblock_index].sb->sb_serial > latest_superblock->sb_serial)) 
-                                 latest_superblock = qnx6_superblocks[superblock_index].sb; // update newest superblock pointer 
-                 } 
-                 else if (qnx6_superblocks[superblock_index].is_mandatory) 
-                 { 
-                         fprintf (- stderr , "error: superblock #%zd invalid: this does not appear to be a qnx6 filesystem\n",-  superblock_index );
 
-                 } 
-         } 
-         if (latest_superblock == NULL) 
-         { 
-                 fprintf (- stderr , "error: no qnx6fs superblock found: this does not appear to be a qnx6 filesystem\n");
 
-         } 
-   
-         // do we want to read or set the volume label ? 
-         if (new_label == NULL) 
-         { 
-                 // read the label from the newest superblock 
-                 memcpy (- label ,-  latest_superblock ->- sb_volumeid , 16);
 
-                 label[16] = 0; 
-   
-                 // print it to the standard output 
-         } 
-         else if (argc == 3) 
-         { 
-                 // update the label in all superblocks, fix checksums and update the blocks 
-                 for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++) 
-                 { 
-                         if (qnx6_superblocks[superblock_index].sb->sb_magic != QNX6FS_SUPERBLOCK_MAGIC) 
-                                 continue; // skip invalid superblocks 
-                         strncpy ((char *)-  qnx6_superblocks [- superblock_index ]- . sb->- sb_volumeid ,-  new_label , sizeof (- qnx6_superblocks [- superblock_index ]- . sb->- sb_volumeid ));
 
-                         qnx6_superblocks[superblock_index].sb->sb_checksum = crc32_be (0, &qnx6_superblocks[superblock_index].bytes[8], 504); // fix the superblock checksum 
-                         if ((fseek (- fp ,-  qnx6_superblocks [- superblock_index ]- . offset,-  SEEK_SET ) != 0)
 
-                             || (fwrite (- qnx6_superblocks [- superblock_index ]- . bytes, sizeof (- qnx6_superblocks [- superblock_index ]- . bytes), 1,-  fp ) != 1))
 
-                         { 
-                                 fprintf (- stderr , "error: can't update superblock #%zd of %s at 0x%zx: %s\n",-  superblock_index ,-  dev_pathname ,-  qnx6_superblocks [- superblock_index ]- . offset, strerror (- errno ));
 
-                                 fprintf (- stderr , "*** FILESYSTEM LEFT INCONSISTENT, USE chkqnx6fs TO FIX ***\n");
 
-                         } 
-                 } 
-         } 
-   
- } 
-