
/***************************************************************************
*
*   Copyright (c) 1998, 1999 Jeff V. Merkey
*   895 West Center Street
*   Orem, Utah  84057
*   jmerkey@utah-nac.org
*
*   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, version 2, 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 are free to modify and re-distribute this program in accordance
*   with the terms specified in the GNU Public License.  The copyright
*   contained in this code is required to be present in any derivative
*   works and you are required to provide the source code for this
*   program as part of any commercial or non-commercial distribution.
*   You are required to respect the rights of the Copyright holders
*   named within this code.
*
*   jmerkey@utah-nac.org is the official maintainer of
*   this code.  You are encouraged to report any bugs, problems, fixes,
*   suggestions, and comments about this software to jmerkey@utah-nac.org
*   or linux-kernel@vger.kernel.org.  New releases, patches, bug fixes, and
*   technical documentation can be found at www.kernel.org.  We will
*   periodically post new releases of this software to www.kernel.org
*   that contain bug fixes and enhanced capabilities.
*
*   Original Authorship      :
*      source code written by Jeff V. Merkey
*
*   Original Contributors    :
*      Jeff V. Merkey
*      Darren Major
*      
*
****************************************************************************
*
*
*   AUTHOR   :  Jeff V. Merkey (jmerkey@utah-nac.org)
*   FILE     :  DIR.C
*   DESCRIP  :  NWFS VFS Dir Module for Linux
*   DATE     :  December 5, 1998
*
*
***************************************************************************/

#include "globals.h"


int nwfs_dentry_validate(struct dentry *dentry, int flags)
{
    struct inode *inode = dentry->d_inode;

#if (VERBOSE)
    NWFSPrint("dentry validate\n");
#endif
    
    if (!inode)
       return 1;

    // see if this inode is the root entry
    if (inode->i_sb->s_root->d_inode == inode)
       return 1;

    // see if this inode is still valid.
    if (is_bad_inode(inode))
       return 0;

    if (atomic_read(&dentry->d_count) > 1)
       return 1;

    return 0;
}

int nwfs_delete_dentry(struct dentry * dentry)
{
#if (VERBOSE)
    NWFSPrint("dentry delete\n");
#endif

    if (!dentry->d_inode)
       return 0;

    if (is_bad_inode(dentry->d_inode))
       return 1;

    return 0;
}

struct dentry_operations nwfs_dentry_operations =
{
    d_revalidate:   nwfs_dentry_validate,
    d_delete:       nwfs_delete_dentry,
};

struct file_operations nwfs_dir_operations = {
    read:      generic_read_dir,
    readdir:   nwfs_dir_readdir,
    ioctl:     nwfs_dir_ioctl,
};

struct inode_operations nwfs_dir_inode_operations =
{
    create:    nwfs_create,
    lookup:    nwfs_dir_lookup,
//    link:      nwfs_link,    // does not work at present.  
    unlink:    nwfs_unlink,
    symlink:   nwfs_symlink,
    mkdir:     nwfs_mkdir,
    rmdir:     nwfs_rmdir,
    mknod:     nwfs_mknod,
    rename:    nwfs_rename,
    setattr:   nwfs_notify_change,
};

int nwfs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
    register ULONG i, count = 0;
    struct inode *inode = filp->f_dentry->d_inode;
    register VOLUME *volume = (VOLUME *) inode->i_sb->u.generic_sbp;
    register HASH *hash = 0;
    HASH *dirHash = 0;

    if (!inode || !S_ISDIR(inode->i_mode))
       return -ENOTDIR;

    i = filp->f_pos;
    switch (i)
    {
       case 0:
	  if (filldir(dirent, ".",  1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
	     goto readdir_end;
	  filp->f_pos++;
          count++;
       case 1:
	  if (filldir(dirent, "..", 2, filp->f_pos,
                      filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
	     goto readdir_end;
	  filp->f_pos++;
          count++;

       default:
          while (1)
          {
             hash = get_subdirectory_record(volume, (filp->f_pos - 2), 
	                                    inode->i_ino,
       				            &dirHash);
             if (!hash)
               break;

             // provide name and root name space (MSDOS) record DirNo
             if (filldir(dirent, hash->Name, hash->NameLength,
			 (hash->Root + 2), hash->Root, DT_UNKNOWN) < 0)

	        goto readdir_end;

             filp->f_pos = (hash->Root + 2);
             filp->f_pos++;
	     count++;
          }
          break;
    }

readdir_end:;

#if (VERBOSE)
    NWFSPrint("readdir count=%d pos1-%d pos2-%d\n", (int)count,
             (int)i, (int)filp->f_pos);
#endif

    return count;
}

int nwfs_dir_ioctl(struct inode * inode, struct file * filp,
		  unsigned int cmd, unsigned long arg)
{

#if (VERBOSE)
    NWFSPrint("ioctl cmd-%d\n", (int)cmd);
#endif

    switch (cmd)
    {
       default:
	  return -EINVAL;
    }
    return 0;
}

struct dentry *nwfs_dir_lookup(struct inode *dir, struct dentry *dentry)
{
    register VOLUME *volume = (VOLUME *) dir->i_sb->u.generic_sbp;
    register ino_t ino;
    struct inode **result = &(dentry->d_inode);

#if (VERBOSE)
    NWFSPrint("lookup %s\n", dentry->d_name.name);
#endif

    *result = NULL;
    if (!dir)
       return ERR_PTR(-ENOENT);

    if (!S_ISDIR(dir->i_mode))
       return ERR_PTR(-ENOENT);

    if ((dentry->d_name.len == 0) || ((dentry->d_name.name[0] == '.') &&
	(dentry->d_name.len == 1)))
    {
       *result = dir;
       return 0;
    }

    ino = get_directory_number(volume,
			       &dentry->d_name.name[0],
			       dentry->d_name.len,
	                       dir->i_ino);
    if (ino)
    {
       *result = iget(dir->i_sb, ino);
       if (!(*result))
       {
          d_add(dentry, NULL); // assume create or mkdir being attempted
          dentry->d_time = 0;
          dentry->d_op = &nwfs_dentry_operations;
          return 0;
       }

       d_add(dentry, *result);
       dentry->d_time = 0;
       dentry->d_op = &nwfs_dentry_operations;
       return 0;
    }
    else
    {
       d_add(dentry, NULL); // assume create or mkdir being attempted
       dentry->d_time = 0;
       dentry->d_op = &nwfs_dentry_operations;
       return 0;
    }
}

