Goto sanos source index

//
// setup.c
//
// Setup program
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inifile.h>

#include <os/dev.h>
#include <os/mbr.h>
#include <os/dfs.h>
#include <os/seg.h>
#include <os/tss.h>
#include <os/syspage.h>

struct section *inst;
char *devname;
struct superblock *super;
struct master_boot_record *mbr;
struct boot_sector *bootsect;

int ldr_start;
int ldr_size;

char ssect[SECTORSIZE];
char bsect[SECTORSIZE];
char msect[SECTORSIZE];
char block[64 * 1024];
char str[128];

//
// doformat
//

int doformat(struct section *sect) {
  char *devname;
  char *fstype;
  int blocksize;
  int quick;
  int cache;
  char options[128];
  int rc;

  // Get formatting properties
  devname = get_property(inst, sect->name, "device", "<none>");
  fstype = get_property(inst, sect->name, "fstype", "dfs");
  blocksize = get_numeric_property(inst, sect->name, "blocksize", 4096);
  quick = get_numeric_property(inst, sect->name, "quick", 0);
  cache = get_numeric_property(inst, sect->name, "cache", 0);

  sprintf(options, "blocksize=%d,cache=%d%s", blocksize, cache, quick ? ",quick" : "");
  printf("Formatting device %s (%s)...\n", devname, options);

  // Format device
  rc = mkfs(devname, fstype, options);
  if (rc < 0) return -1;
  printf("format complete\n");
  return 0;
}

//
// install_loader
//

int install_loader(char *devname, char *loader, char *krnlopts) {
  int n;
  int dev;
  int ldr;
  int rc;
  int blocksize;
  int size;
  char *image;

  sprintf(str, "/dev/%s", devname);
  printf("Installing loader %s on %s\n", loader, str);

  // Open device
  dev = open(str, O_RDWR | O_BINARY);
  if (dev < 0) return -1;

  // Read loader image
  ldr = open(loader, O_BINARY);
  if (ldr < 0) return -1;

  size = fstat(ldr, NULL);
  image = (char *) malloc(size);
  if (!image) return -1;

  rc = read(ldr, image, size);
  if (rc < 0) return -1;

  close(ldr);

  // Read super block from device
  rc = lseek(dev, 1 * SECTORSIZE, SEEK_SET);
  if (rc < 0) return -1;

  rc = read(dev, ssect, SECTORSIZE);
  if (rc < 0) return -1;

  super = (struct superblock *) ssect;
  blocksize = 1 << super->log_block_size;

  // Calculate loader start and size in sectors (used by bootstrap)
  ldr_start = super->first_reserved_block * (blocksize / SECTORSIZE);
  ldr_size = size / SECTORSIZE;
  if (size > (int) super->reserved_blocks * blocksize) {
    printf("Loader too big\n");
    errno = EIO;
    return -1;
  }

  // Patch kernel options into image
  if (krnlopts) {
    int optspos;

    if (strlen(krnlopts) > KRNLOPTS_LEN - 1) {
      printf("Kernel options too long\n");
      errno = EBUF;
      return -1;
    }
    
    optspos = *(unsigned short *) (image + KRNLOPTS_POSOFS);
    strcpy(image + optspos, krnlopts);
  }

  // Install loader into image
  rc = lseek(dev, super->first_reserved_block * blocksize, SEEK_SET);
  if (rc < 0) return -1;

  for (n = 0; n < size / blocksize; n++) {
    rc = write(dev, image + n * blocksize, blocksize);
    if (rc < 0) return -1;
  }

  close(dev);
  free(image);

  return 0;
}

//
// install_boot_sector
//

int install_boot_sector(char *devname, char *bootstrap) {
  int dev;
  int boot;
  int disk;
  int rc;
  int partno;
  int partofs;
  char diskname[16];

  sprintf(str, "/dev/%s", devname);
  printf("Installing boot sector %s on %s\n", bootstrap, str);

  // Read bootstrap
  boot = open(bootstrap, O_BINARY);
  if (boot < 0) return -1;

  rc = read(boot, bsect, SECTORSIZE);
  if (rc < 0) return -1;

  close(boot);

  // Check for partitioned disk
  if (strlen(devname) == 4) {
    partno = devname[3] - 'a';
    if (partno < 0 || partno > 3) {
      printf("Invaid partition\n");
      errno = EINVAL;
      return -1;
    }

    // Read master boot record and get partition offset
    sprintf(diskname, "/dev/%c%c%c", devname[0], devname[1], devname[2]);
    disk = open(diskname, O_BINARY);
    if (disk < 0) return -1;

    rc = read(disk, msect, SECTORSIZE);
    if (rc < 0) return -1;

    close(disk);

    mbr = (struct master_boot_record *) msect;
    if (mbr->signature != MBR_SIGNATURE) {
      printf("Invalid signature in master boot record\n");
      errno = EIO;
      return -1;
    }

    partofs = mbr->parttab[partno].relsect;
    //printf("Installing on partition %d offset %d\n", partno, partofs);
  } else {
    partofs = 0;
  }

  // Set boot sector parameters
  bootsect = (struct boot_sector *) bsect;
  if (bootsect->signature != MBR_SIGNATURE) {
    printf("Invalid signature in bootstrap");
    errno = EINVAL;
    return -1;
  }
  
  bootsect->ldrstart = ldr_start + partofs;
  bootsect->ldrsize = ldr_size;

  // Write boot sector to target device
  dev = open(str, O_RDWR | O_BINARY);
  if (dev < 0) return -1;

  rc = lseek(dev, 0 * SECTORSIZE, SEEK_SET);
  if (rc < 0) return -1;

  rc = write(dev, bsect, SECTORSIZE);
  if (rc < 0) return -1;

  close(dev);

  return 0;
}

//
// dosysprep
//

int dosysprep(struct section *sect) {
  char *devname;
  char *bootstrap;
  char *loader;
  char *krnlopts;
  int rc;

  // Get properties
  devname = get_property(inst, sect->name, "device", "<none>");
  bootstrap = get_property(inst, sect->name, "bootstrap", "/boot/boot");
  loader = get_property(inst, sect->name, "loader", "/boot/osldr.dll");
  krnlopts = get_property(inst, sect->name, "options", "");

  // Install loader
  rc = install_loader(devname, loader, krnlopts);
  if (rc < 0) return -1;

  // Install boot sector
  rc = install_boot_sector(devname, bootstrap);
  if (rc < 0) return -1;

  return 0;
}

//
// dokernel
//

int dokernel(struct section *sect) {
  char *kernel;
  char *target;
  int fin;
  int fout;
  int rc;
  int size;
  int left;
  int bytes;
  char targetfn[MAXPATH];

  // Get properties
  kernel = get_property(inst, sect->name, "kernel", "/boot/krnl.dll");
  target = get_property(inst, sect->name, "target", "/mnt/boot");

  printf("Installing kernel %s\n", kernel);

  // Open source kernel file
  fin = open(kernel, O_BINARY);
  if (fin < 0) return -1;

  size = fstat(fin, NULL);

  // Make sure /boot directory exists on target
  if (stat(target, NULL) < 0) {
    rc = mkdir(target, 0755);
    if (rc < 0) return -1;
  }

  // Install kernel on target using reserved inode
  sprintf(targetfn, "%s/krnl.dll", target);
  fout = open(targetfn, O_SPECIAL | (DFS_INODE_KRNL << 24));
  if (fout < 0) return -1;
  fchmod(fout, 0644);

  left = size;
  while (left > 0) {
    bytes = read(fin, block, sizeof block);
    if (!bytes) {
      errno = EIO;
      return -1;
    }
    if (bytes < 0) return -1;

    rc = write(fout, block, bytes);
    if (rc < 0) return -1;

    left -= bytes;
  }

  close(fin);
  close(fout);

  return 0;
}

//
// domount
//

int domount(struct section *sect) {
  char *mntfrom;
  char *mntto;
  char *fstype;
  char *opts;
  int rc;

  // Get properties
  mntfrom = get_property(inst, sect->name, "mntfrom", "hd0a");
  mntto = get_property(inst, sect->name, "mntto", "/mnt");
  fstype = get_property(inst, sect->name, "fstype", "dfs");
  opts = get_property(inst, sect->name, "opts", NULL);

  // Mount file system
  printf("Mounting filesystem %s on %s\n", mntfrom, mntto);
  rc = mount(fstype, mntto, mntfrom, opts);
  if (rc < 0) return -1;

  return 0;
}

//
// dounmount
//

int dounmount(struct section *sect) {
  char *path;
  int rc;

  // Get properties
  path = get_property(inst, sect->name, "path", "/mnt");

  // Unmount file system
  printf("Unmounting filesystem %s\n", path);
  rc = umount(path);
  if (rc < 0) return -1;

  return 0;
}

//
// domkdirs
//

int domkdirs(struct section *sect) {
  struct property *prop;
  char *dirname;
  int rc;

  prop = sect->properties;
  while (prop) {
    dirname = prop->name;
    printf("Creating directory %s\n", dirname);

    rc = mkdir(dirname, 0755);
    if (rc < 0) return -1;

    prop = prop->next;
  }

  return 0;
}

//
// copy_file
//

int copy_file(char *srcfn, char *dstfn) {
  int fin;
  int fout;
  int bytes;
  int rc;
  struct stat st;
  struct utimbuf ut;

  fin = open(srcfn, O_BINARY);
  if (fin < 0) return fin;

  rc = fstat(fin, &st);
  if (rc < 0) return -1;

  fout = open(dstfn,  O_CREAT | O_EXCL | O_BINARY, S_IREAD | S_IWRITE);
  if (fout < 0) return -1;

  fchmod(fout, st.st_mode);
  fchown(fout, st.st_uid, st.st_gid);
  
  ut.modtime = st.st_mtime;
  ut.ctime = st.st_ctime;
  ut.actime = st.st_atime;
  futime(fout, &ut);

  while ((bytes = read(fin , block, sizeof block)) > 0) {
    rc = write(fout, block, bytes);
    if (rc < 0) return -1;
  }

  if (bytes < 0) return -1;

  close(fin);
  close(fout);

  return 0;
}

//
// copy_dir
//

int copy_dir(char *srcdir, char *dstdir) {
  struct copyitem {
    char srcdir[MAXPATH];
    char dstdir[MAXPATH];
    struct copyitem *next;
  };

  struct copyitem *head;
  struct copyitem *tail;
  struct copyitem *next;
  int dir;
  struct direntry dirp;
  struct stat64 buf;
  char srcfn[MAXPATH];
  char dstfn[MAXPATH];
  int rc;

  head = tail = (struct copyitem *) malloc(sizeof(struct copyitem));
  if (!head) return -1;
  strcpy(head->srcdir, srcdir);
  strcpy(head->dstdir, dstdir);
  head->next = NULL;
  
  while (head) {
    dir = _opendir(head->srcdir);
    if (dir < 0) return -1;

    while (_readdir(dir, &dirp, 1) > 0) {
      sprintf(srcfn, "%s/%s", head->srcdir, dirp.name);
      sprintf(dstfn, "%s/%s", head->dstdir, dirp.name);

      rc = stat64(srcfn, &buf);
      if (rc < 0) return -1;

      if ((buf.st_mode & S_IFMT) == S_IFDIR) {
        printf("Creating directory %s\n", dstfn);
        rc = mkdir(dstfn, 0755);
        if (rc < 0) return -1;

        tail->next = (struct copyitem *) malloc(sizeof(struct copyitem));
        if (!tail->next) return -1;
        tail = tail->next;
        strcpy(tail->srcdir, srcfn);
        strcpy(tail->dstdir, dstfn);
        tail->next = NULL;
      } else {
        printf("Copying %s to %s\n", srcfn, dstfn);
        rc = copy_file(srcfn, dstfn);
        if (rc < 0) return -1;
      }
    }

    close(dir);

    next = head->next;
    free(head);
    head = next;
  }

  return 0;
}

//
// docopy
//

int docopy(struct section *sect) {
  struct property *prop;
  char *srcfn;
  char *dstfn;
  struct stat64 buf;
  int rc;

  prop = sect->properties;
  while (prop) {
    dstfn = prop->name;
    srcfn = prop->value;

    printf("Installing %s\n", dstfn);
    
    rc = stat64(srcfn, &buf);
    if (rc < 0) return -1;

    if ((buf.st_mode & S_IFMT) == S_IFDIR) {
      printf("Creating directory %s\n", dstfn);
      rc = mkdir(dstfn, 0755);
      if (rc < 0) return -1;

      rc = copy_dir(srcfn, dstfn);
      if (rc < 0) return -1;
    } else {
      rc = copy_file(srcfn, dstfn);
      if (rc < 0) return -1;
    }

    prop = prop->next;
  }

  return 0;
}

//
// runscript
//

int runscript(char *scriptname) {
  struct section *scriptsect;
  struct property *prop;

  scriptsect = find_section(inst, scriptname);
  if (!scriptsect) {
    printf("Unable to find script section %s\n", scriptname);
    return -EINVAL;
  }

  prop = scriptsect->properties;
  while (prop) {
    char *action;
    char *scriptname;
    int rc;
    struct section *scriptblock;
    
    action = prop->name;
    scriptname = prop->value ? prop->value : prop->name;

    scriptblock = find_section(inst, scriptname);
    if (!scriptblock) {
      printf("Unable to find script block %s\n", scriptname);
      errno = EINVAL;
      return -1;
    }

    if (strcmp(action, "format") == 0) {
      rc = doformat(scriptblock);
    } else if (strcmp(action, "sysprep") == 0) {
      rc = dosysprep(scriptblock);
    } else if (strcmp(action, "mount") == 0) {
      rc = domount(scriptblock);
    } else if (strcmp(action, "kernel") == 0) {
      rc = dokernel(scriptblock);
    } else if (strcmp(action, "mkdirs") == 0) {
      rc = domkdirs(scriptblock);
    } else if (strcmp(action, "copy") == 0) {
      rc = docopy(scriptblock);
    } else if (strcmp(action, "unmount") == 0) {
      rc = dounmount(scriptblock);
    } else {
      printf("Unknown action '%s' in script block %s\n", action, prop->name);
      errno = EINVAL;
      return -1;
    }

    if (rc < 0) {
      printf("Error %d (%s) performing %s\n", errno, strerror(errno), prop->name);
      return -1;
    }
    
    prop = prop->next;
  }

  return 0;
}

//
// main
//

int main(int argc, char *argv[]) {
  char *instfn;
  char *prodname;
  char *prodvers;
  char *scriptname;
  int rc;

  if (argc == 2) {
    instfn = argv[1];
  } else {
    instfn = "/etc/setup.ini";
  }

  // Get setup properties
  inst = read_properties(instfn);
  if (!inst) {
    printf("Error reading install file, %s\n", instfn);
    return 2;
  }

  prodname = get_property(inst, "setup", "product", gettib()->peb->osname);
  prodvers = get_property(inst, "setup", "version", "1.0");
  scriptname = get_property(inst, "setup", "actions", "actions");

  printf("Installing %s version %s\n", prodname, prodvers);

  // Perform install script
  rc = runscript(scriptname);
  if (rc < 0) {
    printf("Installation failed\n");
    free_properties(inst);
    return 1;
  }

  // Installation successfull
  printf("%s installed\n", prodname);
  free_properties(inst);

  return 0;
}