home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 1999 November / VPR9911B.ISO / misc / src / trees / syslinux-1.42 / syslinux.c < prev    next >
C/C++ Source or Header  |  1998-12-05  |  10KB  |  425 lines

  1. #ident "$Id: syslinux.c,v 1.9 1998/04/15 03:07:25 hpa Exp $"
  2. /* ----------------------------------------------------------------------- *
  3.  *   
  4.  *   Copyright 1998 H. Peter Anvin - All Rights Reserved
  5.  *
  6.  *   This program is free software; you can redistribute it and/or modify
  7.  *   it under the terms of the GNU General Public License as published by
  8.  *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
  9.  *   USA; either version 2 of the License, or (at your option) any later
  10.  *   version; incorporated herein by reference.
  11.  *
  12.  * ----------------------------------------------------------------------- */
  13.  
  14. /*
  15.  * syslinux.c - Linux installer program for SYSLINUX
  16.  *
  17.  * This program ought to be portable.  I hope so, at least.
  18.  *
  19.  * HPA note: this program needs too much privilege.  We should probably
  20.  * access the filesystem directly like mtools does so we don't have to
  21.  * mount the disk.  Either that or if Linux gets an fmount() system call
  22.  * we probably could do the mounting ourselves, and make this program
  23.  * setuid safe.
  24.  *
  25.  */
  26.  
  27. #include <alloca.h>
  28. #include <errno.h>
  29. #include <fcntl.h>
  30. #include <mntent.h>
  31. #include <paths.h>
  32. #include <stdio.h>
  33. #include <string.h>
  34. #include <unistd.h>
  35. #include <sys/stat.h>
  36. #include <sys/types.h>
  37. #include <sys/wait.h>
  38.  
  39. #ifndef _PATH_MOUNT
  40. #define _PATH_MOUNT "/bin/mount"
  41. #endif
  42.  
  43. #ifndef _PATH_UMOUNT
  44. #define _PATH_UMOUNT "/bin/umount"
  45. #endif
  46.  
  47. extern unsigned char bootsect[];
  48. extern unsigned int  bootsect_len;
  49.  
  50. extern unsigned char ldlinux[];
  51. extern unsigned int  ldlinux_len;
  52.  
  53. extern void make_stupid(void);    /* Patching routine for stupid mode */
  54.  
  55. char *program;            /* Name of program */
  56. char *device;            /* Device to install to */
  57. uid_t euid;            /* Our current euid */
  58.  
  59. enum bs_offsets {
  60.   bsJump          = 0x00,
  61.   bsOemName       = 0x03,
  62.   bsBytesPerSec   = 0x0b,
  63.   bsSecPerClust   = 0x0d,
  64.   bsResSectors    = 0x0e,
  65.   bsFATs          = 0x10,
  66.   bsRootDirEnts   = 0x11,
  67.   bsSectors       = 0x13,
  68.   bsMedia         = 0x15,
  69.   bsFATsecs       = 0x16,
  70.   bsSecPerTrack   = 0x18,
  71.   bsHeads         = 0x1a,
  72.   bsHiddenSecs    = 0x1c,
  73.   bsHugeSectors   = 0x20,
  74.   bsDriveNumber   = 0x24,
  75.   bsReserved1     = 0x25,
  76.   bsBootSignature = 0x26,
  77.   bsVolumeID      = 0x27,
  78.   bsVolumeLabel   = 0x2b,
  79.   bsFileSysType   = 0x36,
  80.   bsCode          = 0x3e,
  81.   bsSignature     = 0x1fe
  82. };
  83.  
  84. #define bsCopyStart bsBytesPerSec
  85. #define bsCopyLen   (bsCode-bsBytesPerSec)
  86.  
  87. /*
  88.  * Access functions for littleendian numbers, possibly misaligned.
  89.  */
  90. static u_int16_t get_16(unsigned char *p)
  91. {
  92.   return (u_int16_t)p[0] + ((u_int16_t)p[1] << 8);
  93. }
  94.  
  95. static u_int32_t get_32(unsigned char *p)
  96. {
  97.   return (u_int32_t)p[0] + ((u_int32_t)p[1] << 8) +
  98.     ((u_int32_t)p[2] << 16) + ((u_int32_t)p[3] << 24);
  99. }
  100.  
  101. void usage(void)
  102. {
  103.   fprintf(stderr, "Usage: %s [-s] device\n", program);
  104.   exit(1);
  105. }
  106.  
  107. int main(int argc, char *argv[])
  108. {
  109.   static unsigned char sectbuf[512];
  110.   unsigned char *dp;
  111.   const unsigned char *cdp;
  112.   int dev_fd, fd;
  113.   struct stat st;
  114.   int nb, left, veryold;
  115.   unsigned int sectors, clusters;
  116.   int err = 0;
  117.   pid_t f, w;
  118.   int status;
  119.   char *mntpath = NULL, mntname[64];
  120.   char *ldlinux_name, **argp, *opt;
  121.   int my_umask;
  122.  
  123.   program = argv[0];
  124.   
  125.   device = NULL;
  126.  
  127.   for ( argp = argv+1 ; *argp ; argp++ ) {
  128.     if ( **argp == '-' ) {
  129.       opt = *argp + 1;
  130.       if ( !*opt )
  131.     usage();
  132.  
  133.       while ( *opt ) {
  134.     if ( *opt == 's' ) {
  135.       make_stupid();    /* Use "safe, slow and stupid" code */
  136.     } else {
  137.       usage();
  138.     }
  139.     opt++;
  140.       }
  141.     } else {
  142.       if ( device )
  143.     usage();
  144.       device = *argp;
  145.     }
  146.   }
  147.  
  148.   if ( !device )
  149.     usage();
  150.  
  151.   /*
  152.    * First make sure we can open the device at all, and that we have
  153.    * read/write permission.
  154.    */
  155.   dev_fd = open(device, O_RDWR);
  156.   if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) {
  157.     perror(device);
  158.     exit(1);
  159.   }
  160.  
  161.   if ( !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) {
  162.     fprintf(stderr, "%s: not a block device or regular file\n", device);
  163.     exit(1);
  164.   }
  165.  
  166.   left = 512;
  167.   dp = sectbuf;
  168.   while ( left ) {
  169.     nb = read(dev_fd, dp, left);
  170.     if ( nb == -1 && errno == EINTR )
  171.       continue;
  172.     if ( nb < 0 ) {
  173.       perror(device);
  174.       exit(1);
  175.     } else if ( nb == 0 ) {
  176.       fprintf(stderr, "%s: no boot sector\n", device);
  177.       exit(1);
  178.     }
  179.     dp += nb;
  180.     left -= nb;
  181.   }
  182.   close(dev_fd);
  183.  
  184.   sync();
  185.   
  186.   /*
  187.    * Check to see that what we got was indeed an MS-DOS boot sector/superblock
  188.    */
  189.  
  190.   if ( sectbuf[bsBootSignature] == 0x29 ) {
  191.     /* It's DOS, and it has all the new nice fields */
  192.  
  193.     veryold = 0;
  194.  
  195.     sectors = get_16(sectbuf+bsSectors);
  196.     sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
  197.     clusters = sectors / sectbuf[bsSecPerClust];
  198.  
  199.     if ( !memcmp(sectbuf+bsFileSysType, "FAT12   ", 8) ) {
  200.       if ( clusters > 4086 ) {
  201.     fprintf(stderr, "%s: ERROR: FAT12 but claims more than 4086 clusters\n",
  202.         device);
  203.     exit(1);
  204.       }
  205.     } else if ( !memcmp(sectbuf+bsFileSysType, "FAT16   ", 8) ) {
  206.       if ( clusters <= 4086 ) {
  207.     fprintf(stderr, "%s: ERROR: FAT16 but claims less than 4086 clusters\n",
  208.         device);
  209.     exit(1);
  210.       }
  211.     } else if ( !memcmp(sectbuf+bsFileSysType, "FAT     ", 8) ) {
  212.       /* OS/2 sets up the filesystem as just `FAT'. */
  213.     } else {
  214.       fprintf(stderr, "%s: filesystem type \"%8.8s\" not supported\n",
  215.           device, sectbuf+bsFileSysType);
  216.       exit(1);
  217.     }
  218.   } else {
  219.     veryold = 1;
  220.  
  221.     if ( sectbuf[bsSecPerClust] & (sectbuf[bsSecPerClust] - 1) ||
  222.      sectbuf[bsSecPerClust] == 0 ) {
  223.       fprintf(stderr, "%s: This doesn't look like a FAT filesystem\n",
  224.           device);
  225.     }
  226.  
  227.     sectors = get_16(sectbuf+bsSectors);
  228.     sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors);
  229.     clusters = sectors / sectbuf[bsSecPerClust];
  230.   }
  231.  
  232.   if ( get_16(sectbuf+bsBytesPerSec) != 512 ) {
  233.     fprintf(stderr, "%s: Sector sizes other than 512 not supported\n",
  234.         device);
  235.     exit(1);
  236.   }
  237.   if ( sectbuf[bsSecPerClust] > 32 ) {
  238.     fprintf(stderr, "%s: Cluster sizes larger than 16K not supported\n",
  239.         device);
  240.   }
  241.  
  242.   /*
  243.    * Now mount the device.  If we are non-root we need to find an fstab
  244.    * entry for this device which has the user flag and the appropriate
  245.    * options set.
  246.    */
  247.   if ( (euid = geteuid()) ) {
  248.     FILE *fstab;
  249.     struct mntent *mnt;
  250.  
  251.     if ( !(fstab = setmntent(MNTTAB, "r")) ) {
  252.       fprintf(stderr, "%s: cannot open " MNTTAB "\n", program);
  253.     }
  254.     
  255.     while ( (mnt = getmntent(fstab)) ) {
  256.       if ( !strcmp(device, mnt->mnt_fsname) &&
  257.        ( !strcmp(mnt->mnt_type, "msdos") ||
  258.          !strcmp(mnt->mnt_type, "umsdos") ||
  259.          !strcmp(mnt->mnt_type, "vfat") ||
  260.          !strcmp(mnt->mnt_type, "uvfat") ||
  261.          !strcmp(mnt->mnt_type, "auto") ) &&
  262.        hasmntopt(mnt, "user") &&
  263.        !hasmntopt(mnt, "ro") &&
  264.        mnt->mnt_dir[0] == '/' &&
  265.        !!hasmntopt(mnt, "loop") == !!S_ISREG(st.st_mode)) {
  266.     /* Okay, this is an fstab entry we should be able to live with. */
  267.  
  268.     mntpath = mnt->mnt_dir;
  269.     break;
  270.       }
  271.     }
  272.     endmntent(fstab);
  273.  
  274.     if ( !mntpath ) {
  275.       fprintf(stderr, "%s: not root and no appropriate entry for %s in "
  276.           MNTTAB "\n", program, device);
  277.       exit(1);
  278.     }
  279.   
  280.     f = fork();
  281.     if ( f < 0 ) {
  282.       perror(program);
  283.       exit(1);
  284.     } else if ( f == 0 ) {
  285.       execl(_PATH_MOUNT, _PATH_MOUNT, mntpath, NULL);
  286.       _exit(255);        /* If execl failed, trouble... */
  287.     }
  288.   } else {
  289.     int i = 0;
  290.  
  291.     /* We're root.  Make a temp dir and pass all the gunky options to mount. */
  292.  
  293.     do {
  294.       sprintf(mntname, "/tmp/syslinux.mnt.%lu.%d", (unsigned long)getpid(), i++);
  295.     } while ( (errno = 0, mkdir(mntname, 0700) == -1) &&
  296.           (errno == EEXIST || errno == EINTR) );
  297.     if ( errno ) {
  298.       perror(program);
  299.       exit(1);
  300.     }
  301.     
  302.     mntpath = mntname;
  303.  
  304.     f = fork();
  305.     if ( f < 0 ) {
  306.       perror(program);
  307.       rmdir(mntpath);
  308.       exit(1);
  309.     } else if ( f == 0 ) {
  310.       if ( S_ISREG(st.st_mode) )
  311.     execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-o", "loop", "-w",
  312.           device, mntpath, NULL);
  313.       else
  314.     execl(_PATH_MOUNT, _PATH_MOUNT, "-t", "msdos", "-w", device, mntpath,
  315.           NULL);
  316.       _exit(255);        /* execl failed */
  317.     }
  318.   }
  319.  
  320.   w = waitpid(f, &status, 0);
  321.   if ( w != f || status ) {
  322.     if ( !euid )
  323.       rmdir(mntpath);
  324.     exit(1);            /* Mount failed */
  325.   }
  326.  
  327.   ldlinux_name = alloca(strlen(mntpath)+13);
  328.   if ( !ldlinux_name ) {
  329.     perror("malloc");
  330.     err = 1;
  331.     goto umount;
  332.   }
  333.   sprintf(ldlinux_name, "%s/ldlinux.sys", mntpath);
  334.  
  335.   unlink(ldlinux_name);
  336.   fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444);
  337.   if ( fd < 0 ) {
  338.     perror(device);
  339.     err = 1;
  340.     goto umount;
  341.   }
  342.  
  343.   cdp = ldlinux;
  344.   left = ldlinux_len;
  345.   while ( left ) {
  346.     nb = write(fd, cdp, left);
  347.     if ( nb == -1 && errno == EINTR )
  348.       continue;
  349.     else if ( nb <= 0 ) {
  350.       perror(device);
  351.       err = 1;
  352.       goto umount;
  353.     }
  354.  
  355.     dp += nb;
  356.     left -= nb;
  357.   }
  358.  
  359.   /*
  360.    * I don't understand why I need this.  Does the DOS filesystems
  361.    * not honour the mode passed to open()?
  362.    */
  363.   my_umask = umask(0777);
  364.   umask(my_umask);
  365.   fchmod(fd, 0444 & ~my_umask);
  366.  
  367.   close(fd);
  368.  
  369. umount:
  370.   f = fork();
  371.   if ( f < 0 ) {
  372.     perror("fork");
  373.     exit(1);
  374.   } else if ( f == 0 ) {
  375.     execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL);
  376.   }
  377.  
  378.   w = waitpid(f, &status, 0);
  379.   if ( w != f || status ) {
  380.     exit(1);
  381.   }
  382.  
  383.   sync();
  384.  
  385.   if ( !euid )
  386.     rmdir(mntpath);
  387.  
  388.   if ( err )
  389.     exit(err);
  390.  
  391.   /*
  392.    * To finish up, write the boot sector
  393.    */
  394.   dev_fd = open(device, O_RDWR);
  395.   if ( dev_fd < 0 ) {
  396.     perror(device);
  397.     exit(1);
  398.   }
  399.  
  400.   /* Copy the old superblock into the new boot sector */
  401.   memcpy(bootsect+bsCopyStart, sectbuf+bsCopyStart, bsCopyLen);
  402.   
  403.   dp = bootsect;
  404.   left = bootsect_len;
  405.   while ( left ) {
  406.     nb = write(dev_fd, dp, left);
  407.     if ( nb == -1 && errno == EINTR )
  408.       continue;
  409.     else if ( nb <= 0 ) {
  410.       perror(device);
  411.       exit(1);
  412.     }
  413.     
  414.     dp += nb;
  415.     left -= nb;
  416.   }
  417.   close(dev_fd);
  418.  
  419.   sync();
  420.  
  421.   /* Done! */
  422.  
  423.   return 0;
  424. }
  425.