/*
 * mig_build_cd.c
 * 
 * Build SAM-FS file entries based on the directory structure of a CD-ROM.
 * 
 */

/*    LSC_disclaimer_begin 
 *
 *  Copyright (c) 2003 Sun Microsystems, Inc.
 *  All rights reserved.
 *
 *  This file is a product of Sun Microsystems, Inc. and is provided for 
 *  unrestricted use provided that this header is included on all media 
 *  and as a part of the software program in whole or part.  Users may 
 *  copy, modify or distribute this file at will.
 *
 *  This file is provided with no support and without any obligation on 
 *  the part of Sun Microsystems, Inc. to assist in its use, correction, 
 *  modification or enhancement.
 *
 *  THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
 *  THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 *  PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 *  SUN MICROSYSTEMS INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 
 *  INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE 
 *  OR ANY PART THEREOF.
 *
 *  IN NO EVENT WILL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY LOST REVENUE 
 *  OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL DAMAGES, EVEN 
 *  IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 *  Sun Microsystems, Inc.
 *
 *    LSC_disclaimer_end                                            
 */


#ifndef lint
static char rcs_id[] = "@(#)$Id: mig_build_cd.c,v 1.4 2003/07/25 19:27:58 jdunn Exp $";
#endif /* lint */
#pragma ident "$Id: mig_build_cd.c,v 1.4 2003/07/25 19:27:58 jdunn Exp $"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <ndbm.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "mig.h"
#include "stat.h"

/*
 * location of CD database -- created by this program, where %s will be the
 * CD directory.  For example, if your cd-path is /cdrom/cdrom0 then it will
 * create a database file at: /usr/tmp/db-cdrom0 
 * NOTE:  This constant must be the same as in mig_cd.c
 */
#define DB_PATHSTR "/usr/tmp/db-%s"


/*
 * Function prototypes  
 */
static void process_dir (char *, DIR *, char *, char *);

/*
 * Globals  
 */
static DBM *db;

void
main (int argc, char **argv)
{
  char *cd_path, *sam_path;
  char *db_name, *next;
  char db_fullpath[MAXPATHLEN];
  DIR *cd_dir, *sam_dir;

  if (argc != 3)
    {
      fprintf (stderr, "Usage: mig_build_cd cd-path sam-path\n");
      exit (1);
    }
  argv++;
  cd_path = strdup (*argv);
  argv++;
  sam_path = strdup (*argv);

  /* peel off the last directory name */
  db_name = next = strtok (strdup (cd_path), "/");
  while ((next = strtok (NULL, "/")) != NULL)
    db_name = next;
  /* now set up the full pathname for the database */
  sprintf (db_fullpath, DB_PATHSTR, db_name);

  /* verify parameters */
  if ((db = dbm_open (db_fullpath, O_RDWR | O_CREAT, 0660)) == NULL)
    {
      fprintf (stderr, "Unable to create/rewrite %s: %s.\n", db_fullpath,
	       strerror (errno));
      exit (1);
    }
  if ((cd_dir = opendir (cd_path)) == NULL)
    {
      fprintf (stderr, "Unable to open %s: %s.\n", cd_path, strerror (errno));
      exit (1);
    }
  if ((sam_dir = opendir (sam_path)) == NULL)
    {
      fprintf (stderr, "Unable to open %s: %s.\n", sam_path, strerror (errno));
      exit (1);
    }
  closedir (sam_dir);

  process_dir (db_name, cd_dir, cd_path, sam_path);

}

static void
process_dir (char *db_name,
	     DIR * cd_dir,
	     char *cd_path,
	     char *sam_path)
{
  static int next_file = 1;
  int cd_path_len, sam_path_len;
  char *cd_file_name, *sam_file_name;
  char cd_full_name[MAXPATHLEN], sam_full_name[MAXPATHLEN];
  char linkname[MAXPATHLEN];
  datum db_key =
  {(char *) &next_file, sizeof (next_file)};
  datum db_entry;
  struct sam_stat fstatus;
  struct stat ufs_stat;
  dirent_t *dir_entry;

  /* Set up path names */
  strncpy (cd_full_name, cd_path, MAXPATHLEN);
  cd_path_len = strlen (cd_full_name);
  /* Address pointer to filename portion of the path */
  cd_file_name = cd_full_name + cd_path_len;

  strncpy (sam_full_name, sam_path, MAXPATHLEN);
  sam_path_len = strlen (sam_full_name);
  /* Address pointer to filename portion of the path */
  sam_file_name = sam_full_name + sam_path_len;

  /* Read a directory entry from the CD */
  while ((dir_entry = readdir (cd_dir)) != NULL)
    {
      int len = strlen (dir_entry->d_name);
      int error;

      /* Skip directories: . and .. */
      if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
	continue;

      if ((cd_path_len + len) >= MAXPATHLEN - 2)
	{
	  fprintf (stderr, "%s/%s would exceed MAXPATHLEN (%d)\n",
		   cd_path, dir_entry->d_name, MAXPATHLEN);
	  continue;
	}
      if ((sam_path_len + len) >= MAXPATHLEN - 2)
	{
	  fprintf (stderr, "%s/%s would exceed MAXPATHLEN (%d)\n",
		   sam_path, dir_entry->d_name, MAXPATHLEN);
	  continue;
	}

      /* Append the directory entry filename to the full path */
      sprintf (cd_file_name, "/%s", dir_entry->d_name);
      sprintf (sam_file_name, "/%s", dir_entry->d_name);

      /* Gather statistics on the file */
      if (lstat (cd_full_name, &ufs_stat) < 0)
	{
	  fprintf (stderr, "lstat: %s: %s\n", cd_full_name, strerror (errno));
	  continue;
	}

      /* copy the stat structure elements into the sam_stat structure */
      memset(&fstatus, 0, sizeof(fstatus));
      fstatus.st_mode = ufs_stat.st_mode;
      fstatus.st_uid = ufs_stat.st_uid;
      fstatus.st_gid = ufs_stat.st_gid;
      fstatus.st_size = ufs_stat.st_size;  /* note that sam_stat can handle
						64 bit file sizes */
      fstatus.st_atime = ufs_stat.st_atim.tv_sec;
      fstatus.st_mtime = ufs_stat.st_mtim.tv_sec;
      fstatus.st_ctime = ufs_stat.st_ctim.tv_sec;

      /* from here on, use the sam_stat structure fstatus */

      /* If linked, attempt to look up the link */
      if (S_ISLNK (fstatus.st_mode))
	{
	  int link_len;

	  if ((link_len = readlink (cd_full_name, linkname, MAXPATHLEN)) <= 0)
	    {
	      fprintf (stderr, "readlink: %s: %s\n", cd_full_name,
		       strerror (errno));
	      continue;
	    }
	  linkname[link_len] = '\0';
	  if (linkname[0] == '/')
	    {
	      fprintf (stderr, "Absolute path names not allowed for links:"
		       " %s -> %s", linkname, sam_full_name);
	      continue;
	    }
	  if (symlink (linkname, sam_full_name) != 0)
	    fprintf (stderr, "symlink: %s -> %s: %s\n", linkname,
		     sam_full_name, strerror (errno));

	  continue;
	}

      /* If it is a directory, recurse down the tree */
      if (S_ISDIR (fstatus.st_mode))
	{
	  DIR *next_dir;

	  if ((next_dir = opendir (cd_full_name)) == NULL)
	    {
	      fprintf (stderr, "Unable to open directory %s: %s\n",
		       cd_full_name, strerror (errno));
	      continue;
	    }
	  /* Make the same structure on the SAM-FS side */
	  if (mkdir (sam_full_name, (fstatus.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))))
	    if (errno != EEXIST)
	      {
		fprintf (stderr, "Unable to make directory %s: %s\n",
			 sam_full_name, strerror (errno));
		continue;
	      }
	  /* Recurse this tree leg */
	  process_dir (db_name, next_dir, cd_full_name, sam_full_name);
	  continue;
	}

      /* Add an entry to the database file */
      db_entry.dptr = cd_full_name;
      db_entry.dsize = strlen (cd_full_name);
      (void) dbm_store (db, db_key, db_entry, DBM_INSERT);
      /* Set up and create a directory entry */

      fstatus.copy[0].position = next_file++;
      fstatus.copy[0].creation_time = time (NULL);
      fstatus.copy[0].media[2] = 'z';
      fstatus.copy[0].media[3] = 'a';
      sprintf (&fstatus.copy[0].vsn[0], db_name);
      sam_mig_create_file (sam_full_name, &fstatus);

    }

  closedir( cd_dir );
}
