Subversion Repositories QNX 8.QNX8 utilities

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. // qnx6label.c -- read/set the volume labels of qnx6fs partitions by Pierre-Marie Baty <pm@pmbaty.com>
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <stdint.h>
  6. #include <stdbool.h>
  7. #include <string.h>
  8. #include <errno.h>
  9. #include <ctype.h>
  10.  
  11.  
  12. #define QNX6FS_SUPERBLOCK_MAGIC 0x68191122
  13.  
  14.  
  15. static uint32_t crc32_be (uint32_t crc, const uint8_t *p, size_t len)
  16. {
  17.         // big endian CRC32 function to recompute a qnx6fs superblock checksum
  18.  
  19.         const uint32_t CRCPOLY_BE = 0x04c11db7;
  20.         size_t bit_index;
  21.  
  22.         while (len--)
  23.         {
  24.                 crc ^= *p++ << 24;
  25.                 for (bit_index = 0; bit_index < 8; bit_index++)
  26.                         crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
  27.         }
  28.  
  29.         return (crc);
  30. }
  31.  
  32.  
  33. int main (int argc, char **argv)
  34. {
  35.         // program entrypoint
  36.  
  37.         // references:
  38.         // https://docs.kernel.org/filesystems/qnx6.html
  39.         // https://github.com/torvalds/linux/blob/master/fs/qnx6/qnx6.h
  40.         // https://github.com/torvalds/linux/blob/master/include/linux/qnx6_fs.h
  41.         // https://github.com/torvalds/linux/blob/master/fs/qnx6/inode.c
  42.  
  43.         typedef struct __attribute__((packed)) qnx6_rootnode_s
  44.         {
  45.                 uint64_t size;
  46.                 uint32_t ptr[16]; // 16 block pointers in sbl/inode
  47.                 uint8_t levels;
  48.                 uint8_t mode;
  49.                 uint8_t spare[6];
  50.         } qnx6_rootnode_t;
  51.         typedef struct __attribute__((packed)) qnx6_superblock_s
  52.         {
  53.                 uint32_t sb_magic; // offset 0 -- "\x22\x11\x19\x68" or 0x68191122
  54.                 uint32_t sb_checksum; // offset 4
  55.                 uint64_t sb_serial; // offset 8
  56.                 uint32_t sb_ctime; // offset 16 -- time the fs was created
  57.                 uint32_t sb_atime; // offset 20 -- last access time
  58.                 uint32_t sb_flags; // offset 24
  59.                 uint16_t sb_version1; // offset 28 -- filesystem version info
  60.                 uint16_t sb_version2; // offset 30 -- filesystem version info
  61.                 uint8_t sb_volumeid[16]; // offset 32
  62.                 uint32_t sb_blocksize; // offset 48
  63.                 uint32_t sb_num_inodes; // offset 52
  64.                 uint32_t sb_free_inodes; // offset 56
  65.                 uint32_t sb_num_blocks; // offset 60
  66.                 uint32_t sb_free_blocks; // offset 64
  67.                 uint32_t sb_allocgroup; // offset 68
  68.                 qnx6_rootnode_t rootnode_inode; // offset 72
  69.                 qnx6_rootnode_t rootnode_bitmap; // offset 152
  70.                 qnx6_rootnode_t rootnode_longfile; // offset 232
  71.                 qnx6_rootnode_t rootnode_unknown; // offset 312
  72.         } qnx6_superblock_t; // size 392
  73.         typedef struct qnx6_superblock_info_s
  74.         {
  75.                 bool is_mandatory;
  76.                 size_t offset;
  77.                 uint8_t bytes[512];
  78.                 qnx6_superblock_t *sb; // points at beginning of bytes array
  79.         } qnx6_superblock_info_t;
  80.  
  81.         qnx6_superblock_info_t qnx6_superblocks[4] =
  82.         {
  83.                 { true,  0x2000,   "", (qnx6_superblock_t *) qnx6_superblocks[0].bytes }, // mandatory first superblock
  84.                 { false, 0x2e00,   "", (qnx6_superblock_t *) qnx6_superblocks[1].bytes },
  85.                 { true,  SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[2].bytes }, // mandatory last superblock
  86.                 { false, SIZE_MAX, "", (qnx6_superblock_t *) qnx6_superblocks[3].bytes },
  87.         };
  88.         qnx6_superblock_t *latest_superblock;
  89.         const char *dev_pathname;
  90.         size_t superblock_index;
  91.         size_t label_offset;
  92.         size_t char_index;
  93.         size_t end_pos;
  94.         uint8_t *p;
  95.         char *new_label;
  96.         char label[17];
  97.         FILE *fp;
  98.  
  99.         // consistency checks
  100.         if ((argc < 2) || (*(argv[1]) == '-'))
  101.         {
  102.                 fprintf (stderr, "usage: qnx6label <device> [new-label]\n");
  103.                 exit (1);
  104.         }
  105.  
  106.         dev_pathname = argv[1]; // read arguments
  107.         new_label = (argc > 2 ? argv[2] : NULL);
  108.  
  109.         // open and read all mandatory superblocks of the passed device pathname
  110.         if ((fp = fopen (dev_pathname, "r+b")) == NULL)
  111.         {
  112.                 fprintf (stderr, "error: can't open %s: %s\n", dev_pathname, strerror (errno));
  113.                 exit (1);
  114.         }
  115.         fseek (fp, 0, SEEK_END);
  116.         end_pos = ftell (fp);
  117.         if (end_pos < 0x4000)
  118.         {
  119.                 fprintf (stderr, "error: %s too small to contain a qnx6 filesystem\n", dev_pathname);
  120.                 exit (1);
  121.         }
  122.         qnx6_superblocks[2].offset = (end_pos & ~0xfff) - 0x1000; // compute mandatory last superblock offset
  123.         qnx6_superblocks[3].offset = (end_pos & ~0xfff) - 0x1000 + 0xe00; // compute optional last superblock offset
  124.         latest_superblock = NULL;
  125.         for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++)
  126.         {
  127.                 // seek at the right offset and read the (supposed) qnx6fs superblock data
  128.                 if ((fseek (fp, qnx6_superblocks[superblock_index].offset, SEEK_SET) != 0)
  129.                     || (fread (qnx6_superblocks[superblock_index].bytes, sizeof (qnx6_superblocks[superblock_index].bytes), 1, fp) != 1))
  130.                 {
  131.                         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));
  132.                         exit (1);
  133.                 }
  134.  
  135.                 // validate that all mandatory superblocks are present and keep track of the newest one
  136.                 if (qnx6_superblocks[superblock_index].sb->sb_magic == QNX6FS_SUPERBLOCK_MAGIC)
  137.                 {
  138.                         if ((latest_superblock == NULL) || (qnx6_superblocks[superblock_index].sb->sb_serial > latest_superblock->sb_serial))
  139.                                 latest_superblock = qnx6_superblocks[superblock_index].sb; // update newest superblock pointer
  140.                 }
  141.                 else if (qnx6_superblocks[superblock_index].is_mandatory)
  142.                 {
  143.                         fprintf (stderr, "error: superblock #%zd invalid: this does not appear to be a qnx6 filesystem\n", superblock_index);
  144.                         exit (1);
  145.                 }
  146.         }
  147.         if (latest_superblock == NULL)
  148.         {
  149.                 fprintf (stderr, "error: no qnx6fs superblock found: this does not appear to be a qnx6 filesystem\n");
  150.                 exit (1);
  151.         }
  152.  
  153.         // do we want to read or set the volume label ?
  154.         if (new_label == NULL)
  155.         {
  156.                 // read the label from the newest superblock
  157.                 memcpy (label, latest_superblock->sb_volumeid, 16);
  158.                 label[16] = 0;
  159.  
  160.                 // print it to the standard output
  161.                 printf ("%s\n", label);
  162.         }
  163.         else if (argc == 3)
  164.         {
  165.                 // update the label in all superblocks, fix checksums and update the blocks
  166.                 for (superblock_index = 0; superblock_index < sizeof (qnx6_superblocks) / sizeof (qnx6_superblocks[0]); superblock_index++)
  167.                 {
  168.                         if (qnx6_superblocks[superblock_index].sb->sb_magic != QNX6FS_SUPERBLOCK_MAGIC)
  169.                                 continue; // skip invalid superblocks
  170.                         strncpy ((char *) qnx6_superblocks[superblock_index].sb->sb_volumeid, new_label, sizeof (qnx6_superblocks[superblock_index].sb->sb_volumeid));
  171.                         qnx6_superblocks[superblock_index].sb->sb_checksum = crc32_be (0, &qnx6_superblocks[superblock_index].bytes[8], 504); // fix the superblock checksum
  172.                         if ((fseek (fp, qnx6_superblocks[superblock_index].offset, SEEK_SET) != 0)
  173.                             || (fwrite (qnx6_superblocks[superblock_index].bytes, sizeof (qnx6_superblocks[superblock_index].bytes), 1, fp) != 1))
  174.                         {
  175.                                 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));
  176.                                 fprintf (stderr, "*** FILESYSTEM LEFT INCONSISTENT, USE chkqnx6fs TO FIX ***\n");
  177.                                 exit (1);
  178.                         }
  179.                 }
  180.         }
  181.  
  182.         fclose (fp);
  183.         exit (0);
  184. }
  185.