Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 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 | } |