/*
 * tools/lib/lvm_dir_cache.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
 *
 * May 1998
 * January,April,July,August,September,December 1999
 *
 *
 * This LVM library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This LVM library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this LVM library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 *
 */

/*
 * Changelog
 *
 *   24/01/1999 - corrected to new devfs name space
 *   22/04/1999 - avoided useless allocation in case of cache hit
 *              - DAC960 (/dev/rd/) support by Matthew Taylor
 *                <M.Taylor@rbgkew.org.uk>
 *   23/07/1999 - added support for Compac Smart Array controller
 *   15/08/1999 - fixed selection of DAC960 devices
 *   29/10/1999 - fixed possible free() bug
 *   24/12/1999 - added /proc/partitions in lvm_dir_cache() support
 *
 */

#include <liblvm.h>

char *dirname = NULL;
int  lvm_dir_cache_hit ( dir_cache_t*, int, dev_t);
void lvm_add_dir_cache ( char*, char*);

static dir_cache_t *dir_cache = NULL;
static int cache_size = 0;


int lvm_dir_cache ( dir_cache_t **dir_cache_ptr) {
   int get_from_proc = LVM_DIR_CACHE_GET_FROM_PROC;
   int d = 0;
   int dirent_count = 0;
   int n = 0;
   FILE *proc = NULL;
   char major[20] = { 0, };
   char minor[20] = { 0, };
   char blocks[20] = { 0, };
   char devname[30] = { 0, };
   static char *devdir[] = {
      "/dev/ida",
      "/dev/ide/hd",
      "/dev/loop",
      "/dev/md",
      "/dev/rd",
      "/dev/sd",
      "/dev",
      NULL
   };
   struct dirent **dirent = NULL;


#ifdef DEBUG
   debug ( "lvm_dir_cache -- CALLED\n");
#endif

   if ( dir_cache_ptr == NULL ||
        ( get_from_proc != TRUE && get_from_proc != FALSE)) return -LVM_EPARAM;

   if ( dir_cache == NULL) {
      if ( get_from_proc == TRUE) {
         if ( ( proc = fopen ( "/proc/partitions", "r")) == NULL) return 0;
         while ( !feof ( proc)) {
             fscanf ( proc, " %s %s %s %s\n", major, minor, blocks, devname);
             if ( atoi ( major) > 0 && atoi ( major) != LVM_BLK_MAJOR) {
                lvm_add_dir_cache ( "/dev", devname);
             }
         }   
         fclose ( proc);
      } else {
         for ( d = 0; devdir[d] != NULL; d++) {
            dirname = devdir[d];
#ifdef DEBUG
            debug ( "lvm_dir_cache -- calling scandir() with %s\n",
                    dirname);
#endif
            if ( ( dirent_count = scandir ( dirname, &dirent,
                                            NULL,
                                            alphasort)) > 0) {
               for ( n = 0; n < dirent_count; n++) {
                  lvm_add_dir_cache ( dirname, dirent[n]->d_name);
               }
   
               for ( n = 0; n < dirent_count; n++) free ( dirent[n]);
               free ( dirent);
            }
#ifdef DEBUG
            debug ( "lvm_dir_cache -- AFTER calling scandir() "
                    "with %s\n", dirname);
#endif
         }
      }
   }

   *dir_cache_ptr = dir_cache;
#ifdef DEBUG
         debug ( "lvm_dir_cache -- LEAVING with cache_size: %d\n", cache_size);
#endif
   return cache_size;
}


void lvm_add_dir_cache ( char *directory, char *devname) {
   char devpath[NAME_LEN] = { 0, };
   dir_cache_t *dir_cache_sav = NULL;
   struct stat stat_b;

   if ( directory == NULL || devname == NULL) return;
   sprintf ( devpath, "%s/%s%c", directory, devname, 0);
   if ( stat ( devpath, &stat_b) == -1) return;
   if ( lvm_check_dev ( &stat_b, TRUE) == FALSE) return;

   if ( lvm_dir_cache_hit ( dir_cache, cache_size,
                            stat_b.st_rdev) == FALSE) {
      dir_cache_sav = dir_cache;
      if ( ( dir_cache =
                realloc ( dir_cache,
                          ( cache_size + 1) *
                            sizeof ( dir_cache_t))) == NULL) {
         fprintf ( stderr, "realloc error in %s [line %d]\n",
                           __FILE__, __LINE__);
         if ( dir_cache_sav != NULL) free ( dir_cache_sav);
         dir_cache_sav = NULL;
         return;
      } else dir_cache_sav = NULL;

      if ( ( dir_cache[cache_size].dev_name =
                malloc ( strlen ( devpath) + 1 )) == NULL) {
         fprintf ( stderr, "malloc error in %s [line %d]\n",
                           __FILE__, __LINE__);
         free ( dir_cache);
         dir_cache = dir_cache_sav = NULL;
         return;
      }

      strcpy ( dir_cache[cache_size].dev_name, devpath);
      dir_cache[cache_size].st_rdev = stat_b.st_rdev;
      dir_cache[cache_size].st_mode = stat_b.st_mode;
      cache_size++;
   }
}


/* try to find an entry in the directory cache */
dir_cache_t *lvm_dir_cache_find ( char *dev_name) {
   int cache_size = 0;
   int d = 0;
   dir_cache_t *dir_cache = NULL;

   if ( dev_name == NULL || pv_check_name ( dev_name) < 0) return NULL;

   cache_size = lvm_dir_cache ( &dir_cache );
   for ( d = 0; d < cache_size; d++) {
      if ( strcmp ( dev_name, dir_cache[d].dev_name) == 0) break;
   }

   return &dir_cache[d];
}


/* check if an entry already exists in the directory cache */
int lvm_dir_cache_hit ( dir_cache_t* dir_cache, int cache_size, dev_t rdev) {
   int d = 0;

   while ( d < cache_size) if ( dir_cache[d++].st_rdev == rdev) return TRUE;
   return FALSE;
}
