/*
 * hostfs.c  -  Routines for reading the ramdisk contents from a host
 *              filesystem
 *
 * Copyright (C) 2002-2003 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: hostfs.c,v 1.2 2003/01/25 23:29:43 gkminix Exp $
 */

#define NEED_BINARY 1
#define NEED_DIR 1
#define NEED_TIME 1
#include <common.h>
#include <nblib.h>
#include "mknbi.h"
#include "dir.h"

#ifndef _MKNBI_H_DOS_
#error Included wrong header file
#endif




/***************************************************************************

			Routines to parse directory tree

 ***************************************************************************/

/*
 * Encode a file name into a unicode integer array. Note that this
 * routine assumes the host to work with latin1 character set.
 */
static void unicode(src, dest, len)
char *src;
utf16_t *dest;
int len;
{
  int i;

  for (i = 0; i < len; i++)
	*(dest++) = (int)(*(src++) & 0x00ff);
  *dest = 0;
}



/*
 * Convert UNIX path name into DOS file name. If the file name doesn't
 * fit into 8.3 format, the name will be converted accoording to DOS
 * rules.
 */
static void cvtname(dsp, namep, path)
struct dir_struct *dsp;
struct fname_struct *namep;
char *path;
{
  char *cp, *fname;
  char numbuf[5];
  int name_end;
  int i, j, k;
  int needs_lfn = FALSE;

  /* Isolate file name */
  if ((cp = strrchr(path, '/')) == NULL)
	cp = path;
  else
	cp++;
  fname = cp;

  /* Copy file name (max. 8 chars) */
  i = 0;
  while (*cp && *cp != '.') {
	if (i < 8)
		namep->name[i++] = toupper(*cp);
	else
		needs_lfn = TRUE;
	cp++;
  }
  name_end = i;
  while (i < 8)
	namep->name[i++] = ' ';
  if (*cp == '.')
	cp++;

  /* Copy file extension (max. 3 chars) */
  i = 0;
  while (*cp) {
	if (i < 3)
		namep->ext[i++] = toupper(*cp);
	else
		needs_lfn = TRUE;
	cp++;
  }
  while (i < 3)
	namep->ext[i++] = ' ';

  /* Clear long file name fields */
  namep->lfn_num = 0;
  if (namep->lfn_name != NULL) {
	free(namep->lfn_name);
	namep->lfn_name = NULL;
  }

  /* Check if the file name already exists after conversion */
  if (!needs_lfn && (findfile(dsp, FALSE, namep->name, namep->ext) != NULL))
	needs_lfn = TRUE;
  if (!needs_lfn)
	return;

  /* For long file names we have to search for a suitable 8.3 name */
  for (i = 1; i <= 999; i++) {
	sprintf(numbuf, "%d", i);
	if ((strlen(numbuf) + name_end) > 8)
		j = 8 - strlen(numbuf);
	else
		j = name_end;
	cp = numbuf;
	namep->name[j++] = '~';
	while (*cp)
		namep->name[j++] = *(cp++);
	if (findfile(dsp, FALSE, namep->name, namep->ext) == NULL) {
		k = roundup(strlen(fname), LFN_CHARS);
		namep->lfn_num = k / LFN_CHARS;
		namep->lfn_name = (utf16_t *)nbmalloc(k * sizeof(utf16_t));
		memset((__u8 *)namep->lfn_name, 0xff, (k * sizeof(utf16_t)));
		unicode(fname, namep->lfn_name, strlen(fname));
		return;
	}
  }
  prnerr1("unable to convert name of file %s into 8.3 format", path);
  exit(EXIT_DOS_DOUBLEFILE);
}



/*
 * Read in the complete directory structure for the ramdisk image.
 */
static struct dir_struct *rdhostdir(parent, path, mtime)
struct dir_struct *parent;
char *path;
time_t mtime;
{
  DIR *dirp;
  char *cp;
  char tmppath[MAXNAMLEN + 1];
  struct stat sbuf;
  struct dirent *ent;
  struct dir_struct *dsp;
  struct dir_struct *tmpdsp;
  struct file_struct *fsp;

  /* Initialize directory structure */
  dsp = (struct dir_struct *)nbmalloc(sizeof(struct dir_struct) + strlen(path));
  strcpy(dsp->src.path, path);
  if (parent != NULL) {
	if ((cp = strrchr(path, '/')) == NULL)
		cp = path;
	else
		cp++;
	if (*cp == '.') {
		dsp->attrib |= ATTR_HIDDEN;
		cp++;
	}
	dsp->attrib = ATTR_DIR;
	cvtname(parent, &(dsp->name), cp);
	gettime(mtime, &(dsp->date), &(dsp->time));
  } else {
	dsp->attrib = ATTR_DIR;
	gettime(time(NULL), &(dsp->date), &(dsp->time));
  }
  dsp->subdirnum = 0;
  dsp->filenum = 0;
  dsp->lfnnum = 0;
  dsp->totsize = 0L;
  dsp->subdirs = NULL;
  dsp->files = NULL;
  dsp->next = NULL;

  /* Open directory */
  if ((dirp = opendir(path)) == NULL) {
	prnerr1("unable to open directory %s", path);
	exit(EXIT_OPENDIR);
  }

  /* Read in whole directory */
  while ((ent = readdir(dirp)) != NULL) {
	if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
		continue;
	sprintf(tmppath, "%s/%s", path, ent->d_name);
	if (stat(tmppath, &sbuf) != 0) {
		prnerr1("unable to stat file %s", tmppath);
		exit(EXIT_STAT);
	}
	if ((sbuf.st_mode & S_IFMT) == S_IFREG) {
		fsp = (struct file_struct *)nbmalloc
				(sizeof(struct file_struct) + strlen(tmppath));
		fsp->attrib = 0;
		cp = ent->d_name;
		if (*cp == '.') {
			fsp->attrib |= ATTR_HIDDEN;
			cp++;
		}
		cvtname(dsp, &(fsp->name), cp);
		if (!(sbuf.st_mode & S_IWRITE))
			fsp->attrib |= ATTR_READONLY;
		if (!strncmp(fsp->name.ext, "SYS", 3) &&
		    (!strncmp(fsp->name.name, "IO      ", 8) ||
		     !strncmp(fsp->name.name, "MSDOS   ", 8) ||
		     !strncmp(fsp->name.name, "KERNEL  ", 8)))
			fsp->attrib |= ATTR_HIDDEN + ATTR_SYSTEM + ATTR_READONLY;
		if (!strncmp(fsp->name.ext, "COM", 3) &&
		    (!strncmp(fsp->name.name, "IBMBIO  ", 8) ||
		     !strncmp(fsp->name.name, "IBMDOS  ", 8)))
			fsp->attrib |= ATTR_HIDDEN + ATTR_SYSTEM + ATTR_READONLY;
		strcpy(fsp->src.path, tmppath);
		gettime(sbuf.st_mtime, &(fsp->date), &(fsp->time));
		fsp->size = (unsigned long)sbuf.st_size;
		fsp->next = dsp->files;
		dsp->totsize += howmany(fsp->size, 1024);
		dsp->lfnnum += fsp->name.lfn_num;
		dsp->files = fsp;
		dsp->filenum++;
	} else if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
		tmpdsp = rdhostdir(dsp, tmppath, sbuf.st_mtime);
		tmpdsp->next = dsp->subdirs;
		dsp->totsize += tmpdsp->totsize;
		dsp->lfnnum += tmpdsp->name.lfn_num;
		dsp->subdirs = tmpdsp;
		dsp->subdirnum++;
	}
  }

  /* Add size of directory to total size, except for root directory  */
  if (parent != NULL)
	dsp->totsize += howmany(DIR_ENTRIES(dsp) * sizeof(struct dos_dir),
									1024);

  /* Close directory and return */
  closedir(dirp);
  return(dsp);
}





/***************************************************************************

			Routines to open and close the host directory

 ***************************************************************************/

/*
 * Open a host directory and read it recursively
 */
void hostopen(name)
char *name;
{
  struct boot_record *bp;
  union {
	time_t        curtime;
	unsigned long serno;
  } serial;

  /* Read the host root directory */
  root_dir = rdhostdir(NULL, name, (time_t)0);

  /* Prepare the boot block */
  boot_size = roundup(boot_data_size, SECTSIZE);
  boot_block = (__u8 *)nbmalloc(boot_size);
  memcpy(boot_block, boot_data, boot_data_size);
  bp = (struct boot_record *)boot_block;
  assign(bp->reserved_sect, htot(boot_size / SECTSIZE));
  bytecpy("MSDOS5.0", bp->oem_name, sizeof(bp->oem_name));

  /* We use the current UNIX time as the serial number */
  serial.serno = 0;
  time(&serial.curtime);
  assign(bp->vol_num.low, htot(low_word(serial.serno)));
  assign(bp->vol_num.high, htot(high_word(serial.serno)));
}



/*
 * Close host directory
 */
void hostclose()
{
  if (boot_block != NULL)
	free(boot_block);
}



/*
 * Copy a file from the host directory into the output file
 */
void hostcopy(clustsize, handle, fsp)
int clustsize;
int handle;
struct file_struct *fsp;
{
  __u8 *buf;
  unsigned long num;
  int eof = FALSE;
  int readsize;
  int infile;

  if ((infile = open(fsp->src.path, O_RDONLY | O_BINARY)) < 0) {
	perror(fsp->src.path);
	exit(EXIT_OPEN);
  }

  buf = (__u8 *)nbmalloc(clustsize);
  for (num = 0; num < fsp->clustnum; num++) {
	memset(buf, 0, clustsize);
	if (!eof) {
		readsize = nbread(buf, clustsize, infile);
		eof = (readsize < clustsize);
	}
	(void)nbwrite(buf, clustsize, handle);
  }
  close(infile);
  free(buf);
}



syntax highlighted by Code2HTML, v. 0.9.1