
/*
 * $Id: store_dir_ufs.c,v 1.7 2000/06/27 22:06:26 hno Exp $
 *
 * DEBUG: section 47    Store Directory Routines
 * AUTHOR: Duane Wessels
 *
 * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
 * ----------------------------------------------------------
 *
 *  Squid is the result of efforts by numerous individuals from the
 *  Internet community.  Development is led by Duane Wessels of the
 *  National Laboratory for Applied Network Research and funded by the
 *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
 *  Duane Wessels and the University of California San Diego.  Please
 *  see the COPYRIGHT file for full details.  Squid incorporates
 *  software developed and/or copyrighted by other sources.  Please see
 *  the CREDITS file for full details.
 *
 *  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
 *  (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 */

#include "squid.h"
#include "libufs.h"

#if HAVE_STATVFS
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#endif

#define DefaultLevelOneDirs     16
#define DefaultLevelTwoDirs     256
#define STORE_META_BUFSZ 4096

/* Common routines between the UFS filesystems. Can be overridden, etc . */


char *
libufs_StoreDirFullPath(SwapDir * SD, ufsinfo_t *ufsinfo, sfileno filn,
    char *fullpath)
{
    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
    if (!fullpath)
        fullpath = fullfilename;
    fullpath[0] = '\0';
    snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X/%08X",
        SD->path,
        ((filn / ufsinfo->l2) / ufsinfo->l2) % ufsinfo->l1,
        (filn / ufsinfo->l2) % ufsinfo->l2,
        filn);
    return fullpath;
}

int
libufs_ValidFileno(SwapDir * SD, ufsinfo_t *ufsinfo, sfileno filn, int flag)
{   
    if (filn < 0)
        return 0;
    /*
     * If flag is set it means out-of-range file number should
     * be considered invalid.
     */
    if (flag)
        if (filn > ufsinfo->map->max_n_files)
            return 0;
    return 1;
}


/*
 * These functions were ripped straight out of the heart of store_dir.c.
 * They assume that the given filenum is on a diskd partiton, which may or
 * may not be true..
 * XXX this evilness should be tidied up at a later date!
 */

int
libufs_DirMapBitTest(SwapDir * SD, ufsinfo_t *ufsinfo, int fn)
{   
    sfileno filn = fn;
    return file_map_bit_test(ufsinfo->map, filn);
}

void
libufs_DirMapBitSet(SwapDir * SD, ufsinfo_t *ufsinfo, int fn)
{   
    sfileno filn = fn;
    file_map_bit_set(ufsinfo->map, filn);
}

void
libufs_DirMapBitReset(SwapDir * SD, ufsinfo_t *ufsinfo, int fn)
{   
    sfileno filn = fn;
    file_map_bit_reset(ufsinfo->map, filn);
}

int
libufs_DirMapBitAllocate(SwapDir * SD, ufsinfo_t *ufsinfo)
{
    int fn;
    fn = file_map_allocate(ufsinfo->map, ufsinfo->suggest);
    file_map_bit_set(ufsinfo->map, fn);
    ufsinfo->suggest = fn + 1;
    return fn;
}  

void
libufs_FreeMemory(SwapDir *SD, ufsinfo_t *ufsinfo)
{
    filemapFreeMemory(ufsinfo->map);
}

/* 
 * Initialise the diskd bitmap
 *  
 * If there already is a bitmap, and the numobjects is larger than currently
 * configured, we allocate a new bitmap and 'grow' the old one into it.
 */
void
libufs_DirInitBitmap(SwapDir * sd, ufsinfo_t *ufsinfo)
{
    if (ufsinfo->map == NULL) {   
        /* First time */
        ufsinfo->map = file_map_create();
    } else if (ufsinfo->map->max_n_files) {
        /* it grew, need to expand */
        /* XXX We don't need it anymore .. */
    }
    /* else it shrunk, and we leave the old one in place */
}

/*
 * Does swapfile number 'fn' belong in cachedir #F0,
 * level1 dir #F1, level2 dir #F2?
 */
int
libufs_FilenoBelongsHere(ufsinfo_t *ufsinfo, int fn, int F0, int F1, int F2)
{   
    int D1, D2;
    int L1, L2;
    int filn = fn;
    assert(F0 < Config.cacheSwap.n_configured);
    L1 = ufsinfo->l1;
    L2 = ufsinfo->l2;
    D1 = ((filn / L2) / L2) % L1;
    if (F1 != D1)
        return 0;
    D2 = (filn / L2) % L2;
    if (F2 != D2)
        return 0;
    return 1;
}



/*
 * General UFS statistics
 * Call this *before* you tack your specific stuff on the end
 */

void
libufs_DirStats(SwapDir * SD, ufsinfo_t *ufsinfo, StoreEntry * sentry)
{   
#if HAVE_STATVFS
    struct statvfs sfs;
#endif
    storeAppendPrintf(sentry, "First level subdirectories: %d\n", ufsinfo->l1)
;   
    storeAppendPrintf(sentry, "Second level subdirectories: %d\n", ufsinfo->l2
);  
    storeAppendPrintf(sentry, "Maximum Size: %d KB\n", SD->max_size);
    storeAppendPrintf(sentry, "Current Size: %d KB\n", SD->cur_size);
    storeAppendPrintf(sentry, "Percent Used: %0.2f%%\n",
        100.0 * SD->cur_size / SD->max_size);
    storeAppendPrintf(sentry, "Filemap bits in use: %d of %d (%d%%)\n",
        ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files,
        percent(ufsinfo->map->n_files_in_map, ufsinfo->map->max_n_files));
#if HAVE_STATVFS
#define fsbtoblk(num, fsbs, bs) \
    (((fsbs) != 0 && (fsbs) < (bs)) ? \
            (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
    if (!statvfs(SD->path, &sfs)) {
        storeAppendPrintf(sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
            fsbtoblk((sfs.f_blocks - sfs.f_bfree), sfs.f_frsize, 1024),
            fsbtoblk(sfs.f_blocks, sfs.f_frsize, 1024),
            percent(sfs.f_blocks - sfs.f_bfree, sfs.f_blocks));
        storeAppendPrintf(sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
            sfs.f_files - sfs.f_ffree, sfs.f_files,
            percent(sfs.f_files - sfs.f_ffree, sfs.f_files));
    }
#endif
    storeAppendPrintf(sentry, "Flags:");
    if (SD->flags.selected)
        storeAppendPrintf(sentry, " SELECTED");
    if (SD->flags.read_only)
        storeAppendPrintf(sentry, " READ-ONLY");
    storeAppendPrintf(sentry, "\n");
}

char *
libufs_SwapSubDir(SwapDir *sd, ufsinfo_t *ufsinfo, int subdirn)
{   
    LOCAL_ARRAY(char, fullfilename, SQUID_MAXPATHLEN);
    assert(0 <= subdirn && subdirn < ufsinfo->l1);
    snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%02X", sd->path, subdirn);
    return fullfilename;
}

int
libufs_VerifyDirectory(const char *path)
{
    struct stat sb;
    if (stat(path, &sb) < 0) {
        debug(20, 0) ("%s: %s\n", path, xstrerror());
        return -1;
    }
    if (S_ISDIR(sb.st_mode) == 0) {
        debug(20, 0) ("%s is not a directory\n", path);
        return -1;
    }
    return 0;
}

/*
 * This function is called by the fs init function.  If this returns < 0,
 * then Squid exits, complains about swap directories not
 * existing, and instructs the admin to run 'squid -z'
 */

int
libufs_VerifyCacheDirs(SwapDir *sd, ufsinfo_t *ufsinfo)
{   
    int j;
    const char *path = sd->path;

    if (libufs_VerifyDirectory(path) < 0)
        return -1;
    for (j = 0; j < ufsinfo->l1; j++) {
        path = libufs_SwapSubDir(sd, ufsinfo, j);
        if (libufs_VerifyDirectory(path) < 0)
            return -1;
    }
    return 0;
}


int
libufs_CreateDirectory(const char *path, int should_exist)
{   
    int created = 0;
    struct stat st;
    getCurrentTime();
    if (0 == stat(path, &st)) {
        if (S_ISDIR(st.st_mode)) {
            debug(20, should_exist ? 3 : 1) ("%s exists\n", path);
        } else {
            fatalf("Swap directory %s is not a directory.", path);
        }
    } else if (0 == mkdir(path, 0755)) {
        debug(20, should_exist ? 1 : 3) ("%s created\n", path);
        created = 1;
    } else {
        fatalf("Failed to make swap directory %s: %s",
            path, xstrerror());
    }
    return created;
}

/*
 * Create the swapdirectories
 */
void
libufs_CreateSwapSubDirs(SwapDir *sd, ufsinfo_t *ufsinfo)
{
    int i, k;
    int should_exist;
    LOCAL_ARRAY(char, name, MAXPATHLEN);
    for (i = 0; i < ufsinfo->l1; i++) {
        snprintf(name, MAXPATHLEN, "%s/%02X", sd->path, i);
        if (libufs_CreateDirectory(name, 0))
            should_exist = 0;
        else
            should_exist = 1;
        debug(47, 1) ("Making directories in %s\n", name);
        for (k = 0; k < ufsinfo->l2; k++) {
            snprintf(name, MAXPATHLEN, "%s/%02X/%02X", sd->path, i, k);
            libufs_CreateDirectory(name, should_exist);
        }
    }

}


