/* Sun style partitioning */

#include <fcntl.h>
#include <unistd.h>

#include "balkan.h"
#include "byteswap.h"

struct singlePartitionTable {
       unsigned char info[128];   /* Informative text string */
       unsigned char spare0[14];
       struct sun_info {
               unsigned char spare1;
               unsigned char id;
               unsigned char spare2;
               unsigned char flags;
       } infos[8];
       unsigned char spare1[246]; /* Boot information etc. */
       unsigned short rspeed;     /* Disk rotational speed */
       unsigned short pcylcount;  /* Physical cylinder count */
       unsigned short sparecyl;   /* extra sects per cylinder */
       unsigned char spare2[4];   /* More magic... */
       unsigned short ilfact;     /* Interleave factor */
       unsigned short ncyl;       /* Data cylinder count */
       unsigned short nacyl;      /* Alt. cylinder count */
       unsigned short ntrks;      /* Tracks per cylinder */
       unsigned short nsect;      /* Sectors per track */
       unsigned char spare3[4];   /* Even more magic... */
       struct sun_partition {
               unsigned int start_cylinder;
               unsigned int num_sectors;
       } parts[8];
       unsigned short magic;      /* Magic number */
       unsigned short csum;       /* Label xor'd checksum */
};

#define SUN_LABEL_MAGIC         0xDABE
#define SECTOR_SIZE             512
#define WHOLE_DISK              5
#define UFS_SUPER_MAGIC         0x00011954

long long llseek(int fd, long long offset, int whence);

int sunpReadTable(int fd, struct partitionTable * table) {
   struct singlePartitionTable singleTable;
   int i, rc, magic;
   unsigned short *p, csum;

   table->maxNumPartitions = 8;

   for (i = 0; i < table->maxNumPartitions; i++)
       table->parts[i].type = -1;

   table->sectorSize = SECTOR_SIZE;

   if (lseek(fd, 0, SEEK_SET) < 0)
       return BALKAN_ERROR_ERRNO;

   if (read(fd, &singleTable, sizeof(singleTable)) != sizeof(singleTable))
       return BALKAN_ERROR_ERRNO;

   if (be16_to_cpu(singleTable.magic) != SUN_LABEL_MAGIC)
       return BALKAN_ERROR_BADMAGIC;

   for (p = (unsigned short *)&singleTable, csum = 0;
        p < (unsigned short *)(&singleTable+1);)
       csum ^= *p++;

   if (csum)
       return BALKAN_ERROR_BADMAGIC;

   for (i = 0; i < 8; i++) {
       if (!singleTable.parts[i].num_sectors) continue;

       table->parts[i].startSector =
           be32_to_cpu(singleTable.parts[i].start_cylinder) *
           be16_to_cpu(singleTable.nsect) *
           be16_to_cpu(singleTable.ntrks);
       table->parts[i].size =
           be32_to_cpu(singleTable.parts[i].num_sectors);
       table->parts[i].type = singleTable.infos[i].id;
   }

   for (i = 0; i < 8; i++) {
       if (table->parts[i].type == -1) continue;

       switch (table->parts[i].type) {
         case 0x83:
           table->parts[i].type = BALKAN_PART_EXT2;
           break;

         case 0x82:
           table->parts[i].type = BALKAN_PART_SWAP;
           break;

         default:
           if (table->parts[i].type != WHOLE_DISK &&
               llseek(fd, (8192 + 0x55c + SECTOR_SIZE *
                           (unsigned long long)table->parts[i].startSector),
                      SEEK_SET) >= 0 &&
               read(fd, &magic, 4) == 4 &&
               (magic == UFS_SUPER_MAGIC ||
                swab32(magic) == UFS_SUPER_MAGIC))
               table->parts[i].type = BALKAN_PART_UFS;
           else
               table->parts[i].type = BALKAN_PART_OTHER;
           break;
       }
   }

   return 0;
}

#ifdef STANDALONE_TEST

void main() {
   int fd;
   int i;
   struct partitionTable table;

   fd = open("/dev/hda", O_RDONLY);

   printf("rc= %d\n", sunpReadTable(fd, &table));

   for (i = 0; i < table.maxNumPartitions; i++) {
       if (table.parts[i].type == -1) continue;

       printf("%d: %x %d\n", i, table.parts[i].type, table.parts[i].size);
   }
}

#endif