Metropoli BBS
VIEWER: fs_misc.c MODE: TEXT (ASCII)
//
// $Header: D:/ext2-os2/RCS/fs_misc.c,v 1.19 1995/08/16 17:37:23 Willm Exp Willm $
//

// Linux ext2 file system driver for OS/2 2.x and WARP - Allows OS/2 to     
// access your Linux ext2fs partitions as normal drive letters.
// OS/2 implementation : Copyright (C) 1995  Matthieu WILLM
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.


#define INCL_DOS
#define INCL_DOSERRORS
#include <os2.h>		// From the "Developer Connection Device Driver Kit" version 2.0
#include <dhcalls.h>     	// From the "Developer Connection Device Driver Kit" version 2.0

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <fsd.h>
#include <fsh.h>

/***********************************************************************************/
/*** Fichiers inclus locaux                                                      ***/
/***********************************************************************************/
#include <linux/stat.h>

#include <os2/ifsdbg.h>
#include <os2/filefind.h>
#include <os2/cdfsd.h>
#include <os2/errors.h>

#include <os2/log.h>         /* Prototypes des fonctions de log.c                      */
#include <os2/volume.h>      /* Prototypes des fonctions de volume.c                   */
#include <os2/files.h>       /* Prototypes des fonctions de files.c                    */

#include <os2/os2proto.h>
#include <os2/os2misc.h>
#include <linux/fs.h>
#include <linux/fs_proto.h>
#include <linux/e2_fs.h>
#include <linux/e2_proto.h>
#include <linux/fcntl.h>
#include <linux/sched.h>

extern unsigned short getuid(void);
extern void setuid(unsigned short uid);

/***********************************************************************************/
/*** Nom et attributs du FSD                                                     ***/
/***********************************************************************************/


char FS_NAME[]             = "ext2";
unsigned long FS_ATTRIBUTE = FSA_LVL7;


/***********************************************************************************/
/*** Errors not in bseerr.h (IFS specific or ext2_os2 specific)                  ***/
/***********************************************************************************/

#define ERROR_VOLUME_NOT_MOUNTED 0xEE00		// IFS specific
#define ERROR_DEVICE_NOT_OPEN    0xEE01		// ext2_os2 specific

/***********************************************************************************/
/*** Some useful defines for FS_OPENCREATE() ...                                 ***/
/***********************************************************************************/

   #define OPEN_ACCESS_MASK               0x0007  /* ---- ---- ---- -111 */
   #define OPEN_ACCESS_EXECUTE            0x0003  /* ---- ---- ---- -100 */

   #define OPEN_SHARE_MASK                0x0070  /* ---- ---- -111 ---- */
   #define OPEN_LOCALITY_MASK             0x0700  /* ---- -111 ---- ---- */
   #define OPEN_ACTION_EXIST_MASK         0x000F  /* ---- ---- ---- 1111 */
   #define OPEN_ACTION_NEW_MASK           0x00F0  /* ---- ---- 1111 ---- */

#define FILE_NONFAT     0x0040		// File is non 8.3 compliant

/***********************************************************************************/
/*** Messages                                                                    ***/
/***********************************************************************************/

extern char Msg_0[];	// Banner

/***********************************************************************************/
/*** Volume related global data                                                  ***/
/***********************************************************************************/

#define NB_MAX_VOLS 26
#define VOL_STATUS_FREE 1
#define VOL_STATUS_MOUNTED 2
#define VOL_STATUS_REMOVED 3

typedef struct {
    unsigned short      hVPB;
    int                 status;
    struct super_block *sb;
} volume_list;

typedef struct {
    UINT32 sem;
    volume_list listvol[NB_MAX_VOLS];
} volume_global_data;

volume_global_data volglobdat;

extern unsigned long Device_Help;

#define THISFILE FILE_TEST_C

/***********************************************************************************/
/*** Some external data ...                                                      ***/
/***********************************************************************************/


extern unsigned long event;		// To be moved somewhere in a .h file

extern unsigned long cache_size;	// To be moved somewhere in a .h file

int Read_Write = 0;			// 0 read only - 1 read write
int auto_fsck  = 1;			// 1 force e2fsck - 0 do not force e2fsck

/***********************************************************************************/
/*** FS_INIT()                                                                   ***/
/***********************************************************************************/
_FS_RET _FS_ENTRY FS_INIT(
                          pchar             szParm,
                          unsigned long         DevHelp,
                          unsigned long _FS_PTR pMiniFSD
                         )
{
    pchar pTmp1;
    int lg;
    int i;
    int Quiet = 0;



    BufPtr    = 0;
    BufOpen   = 0;

    Device_Help = DevHelp;

        
    volglobdat.sem = 0;
    for (i = 0; i < NB_MAX_VOLS; i++) {
        volglobdat.listvol[i].hVPB   = 0;
        volglobdat.listvol[i].status = VOL_STATUS_FREE;
        volglobdat.listvol[i].sb     = 0;
    }


    
    if (szParm != 0) {
        strupr(szParm);
        for (pTmp1 = strtok(szParm, "-/") ; pTmp1 != NULL ; pTmp1 = strtok(NULL, "-/")) {
              if (pTmp1 != NULL) {
                  //
                  // Per volume disk cache size.
                  //
                  if (strncmp(pTmp1, "CACHE:", sizeof("CACHE:") - 1) == 0) {
                      cache_size = (unsigned long)atol(pTmp1 + sizeof("CACHE:") - 1);
                      cache_size = ((cache_size * 1024) / 65536) * 65536;
                      if ((cache_size < 65536) || (cache_size > NB_MAX_SEL_CACHE * 65536)) {
                          DosWrite(1, "EXT2-OS2 : Invalid cache size - Using 256 Kb\r\n", sizeof("EXT2-OS2 : Invalid cache size - Using 256 Kb\r\n"), &lg);
                          cache_size = 256L * 1024L;
                      }
                      continue;
                  }

                  //
                  // Quiet initialization.      
                  //
                  if (strncmp(pTmp1, "Q", sizeof("Q") - 1) == 0) {
                      Quiet = 1;
                      continue;
                  }

                  //
                  // Read/Write access enabled  
                  //
                  if (strncmp(pTmp1, "RW", sizeof("RW") - 1) == 0) {
                      Read_Write = 1;
                      continue;
                  }

                  //
                  // Disable automatic e2fsck when Linux mounts an ext2fs partition "touched"
		  // by OS/2.
                  //
                  if (strncmp(pTmp1, "NO_AUTO_FSCK", sizeof("NO_AUTO_FSCK") - 1) == 0) {
                      auto_fsck = 0;
                      continue;
                  }

                  //
                  // Unknown command line option
                  //
//                  DosWrite(1, "EXT2-OS2 : Unknown option ", sizeof("EXT2-OS2 : Unknown option ") - 1, &lg);
//                  DosWrite(1, pTmp1, strlen(pTmp1), &lg);
//                  DosWrite(1, "\r\n", sizeof("\r\n") - 1, &lg);
              }
        }
    }
    if (!Quiet) {
        DosWrite(1, Msg_0, strlen(Msg_0), &lg);
    }
    return NO_ERROR;

} /*** FS_INIT() ***/


_FS_RET _FS_ENTRY FS_CHGFILEPTR(
                            struct sffsi _FS_PTR psffsi,
                            struct sffsd _FS_PTR psffsd,
                            long                 offset,
                            unsigned short       type,
                            unsigned short       IOflag
                           )
{
    off_t               newfileptr;
    struct super_block *sb;
    struct file        *p_file;

    /*******************************************************************/
    /*** Gets the superblock from psffsi                             ***/
    /*******************************************************************/
    sb = getvolume(psffsi->sfi_hVPB);
    /*******************************************************************/

    /*******************************************************************/
    /*** Gets the file structure from psffsd                         ***/
    /*******************************************************************/
    if (!(p_file = ((_sffsd _FS_PTR)psffsd)->p_file)) {
        kernel_printf("FS_CHGFILEPTR() - p_file = NULL");
        return ERROR_INVALID_PARAMETER;
    }
    /*******************************************************************/


    switch(type) {
        case CFP_RELBEGIN :
#ifdef FS_TRACE
            kernel_printf("FS_CHFILEPTR( ino = %lu ) - Type CFP_RELBEGIN", p_file->f_inode->i_ino);
#endif
            newfileptr = offset;
            break;

        case CFP_RELCUR :
#ifdef FS_TRACE
            kernel_printf("FS_CHFILEPTR( ino = %lu ) - Type CFP_RELCUR", p_file->f_inode->i_ino);
#endif
            newfileptr = psffsi->sfi_position + offset;
            break;

        case CFP_RELEND :
#ifdef FS_TRACE
            kernel_printf("FS_CHFILEPTR( ino = %lu ) - Type CFP_RELEND", p_file->f_inode->i_ino);
#endif
            newfileptr = p_file->f_inode->i_size + offset;
            break;

        default :
            kernel_printf("FS_CHFILEPTR( ino = %lu ) : unknown type", p_file->f_inode->i_ino);
            return ERROR_INVALID_PARAMETER;
    }

    //
    // Offsets below 0 should normally be supported for DOS box requests
    //
    if (newfileptr < 0) {
        kernel_printf("FS_CHGFILEPTR - new file pointer is < 0");
	return ERROR_INVALID_PARAMETER;
    }

    p_file->f_pos        = newfileptr;
    p_file->f_reada      = 0;
    p_file->f_version    = ++event;

    psffsi->sfi_position = newfileptr;
    return NO_ERROR;
}


_FS_RET _FS_ENTRY FS_CLOSE(
                       unsigned short          type,
                       unsigned short          IOflag,
                       struct sffsi    _FS_PTR psffsi,
                       struct sffsd    _FS_PTR psffsd
                      )
{
    struct super_block * sb;
    struct file        * p_file;
    int                  rc;

#ifdef FS_TRACE
    kernel_printf("FS_CLOSE(ino = %lu, type = %d)",  ((_sffsd _FS_PTR)psffsd)->p_file->f_inode->i_ino, type);
#endif

    //
    // Gets the superblock from psffsi
    //
    if (!(sb = getvolume(psffsi->sfi_hVPB))) {
       os2_panic("FS_CLOSE - Unable to retrieve superblock");
    }

    //
    // Gets the file structure from psffsd
    //
    if (!(p_file = ((_sffsd _FS_PTR)psffsd)->p_file)) {
       os2_panic("FS_CLOSE - p_file = NULL");
    }


    //
    // The doc isn't clear about the role of the 'type' parameter. It seems we must 
    // only free the resources (file structure in sffsd) at FS_CL_FORSYS time. Otherwise
    // we'' receive an empty sffsd somewhere else !
    // For other 'type' values, maybe we could do a flush ...
    //
    if (type != FS_CL_FORSYS) {
#ifdef FS_TRACE
        kernel_printf("***** Non final system close **** - sffsi->sfi_type = %d - Type = %d", psffsi->sfi_type, type);
#endif
        return NO_ERROR;
    } /* endif */

    //
    // Final close for the system
    //
    if ((type == FS_CL_FORSYS) && (Read_Write) && (p_file->f_inode->i_ino != INODE_DASD)) {
	if (p_file->f_op && p_file->f_op->release) {
            p_file->f_op->release(p_file->f_inode, p_file);
        } else {
	    os2_panic("FS_CLOSE - p_file->f_op == NULL : shouldn't occur in this release");
        }
    }

    //
    // Closes the file
    //
    if ((rc = _close(sb, &p_file)) != NO_ERROR) {
        fs_err(FUNC_FS_CLOSE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
        return rc;
    }

    //
    // Clean up of sffsd (safety purposes ...)
    //
    memset(psffsd, 0, sizeof(struct sffsd));

    return NO_ERROR;
}

_FS_RET _FS_ENTRY FS_COMMIT(
                            unsigned short         type,
                            unsigned short         IOflag,
                            struct sffsi   _FS_PTR psffsi,
                            struct sffsd   _FS_PTR psffsd
)
{

#ifdef FS_TRACE
    kernel_printf("FS_COMMIT");
#endif

    if (Read_Write) {
        return NO_ERROR;		// we must fsync here
    } else {
        return ERROR_WRITE_PROTECT;
    }
}

_FS_RET _FS_ENTRY FS_COPY(
                          unsigned short         flag ,
                          struct cdfsi   _FS_PTR pcdfsi,
                          struct cdfsd   _FS_PTR pcdfsd,
                          char           _FS_PTR pSrc,
                          unsigned short         iSrcCurrDirEnd,
                          char           _FS_PTR pDst,
                          unsigned short         iDstCurrDirEnd,
                          unsigned short         nameType
)
{

#ifdef FS_TRACE
    kernel_printf("FS_COPY(%s -> %s)", pSrc, pDst);
#endif

    if (Read_Write) {
	return ERROR_CANNOT_COPY;	// Doscall1.dll should emulate it for us ...
					// until someone writes a better COPY routine :-)
    } else {
        return ERROR_WRITE_PROTECT;
    }
}


_FS_RET _FS_ENTRY FS_DELETE(
                            struct cdfsi   _FS_PTR pcdfsi,
                            struct cdfsd   _FS_PTR pcdfsd,
                            char           _FS_PTR pFile,
                            unsigned short         iCurDirEnd
                           )
{
    int                 rc;       /* return code       */
    struct super_block *sb;       /* volume descriptor */
    struct inode       *dir;
    struct file        *filp;
    ino_t               ino_no;
    char              parent[CCHMAXPATH];
    char              name[CCHMAXPATH];
    id_t myid;
    UINT32 DOSmode;
    umode_t		i_mode;

    //
    // Trace
    //
#ifdef FS_TRACE
    kernel_printf("FS_DELETE( %s )", pFile);
#endif

    //
    // Process identification (to recognize DOS box requests)
    //
    if ((rc = FSH_QSYSINFO(2, (pchar)&myid, sizeof(id_t))) != NO_ERROR) {
        fs_log("erreur FSH_QSYSINFO() dans FS_DELETE");
        return rc;
    }
    if (myid.pdb != 0) {
        DOSmode = OPENMODE_DOSBOX;
    } else {
        DOSmode = 0;
    }

    if (Read_Write) {

    //
    // *** This is UGLY ... but it works !
    //



        sb = getvolume(pcdfsi->cdi_hVPB);

        if ((filp = _open_by_name(sb, pFile, OPENMODE_READONLY | DOSmode)) == NULL) {
#ifdef FS_TRACE
            fs_err(FUNC_FS_DELETE, FUNC_OPEN_BY_NAME, -1, THISFILE, __LINE__);
#endif
            return ERROR_FILE_NOT_FOUND;
        } /* end if */
        i_mode = filp->f_inode->i_mode;
        if ((rc = _close(sb, &filp)) != NO_ERROR) {
            fs_err(FUNC_FS_DELETE, FUNC_CLOSE, rc, THISFILE, __LINE__);
            return -1;
        }
        if (!S_ISREG(i_mode)) {
#ifdef FS_TRACE
    	    kernel_printf("FS_DELETE - %s is not a regular file", pFile);
#endif
            return ERROR_ACCESS_DENIED;
        }
        if ((!(i_mode & S_IWUSR)) && 
            (!(i_mode & S_IWGRP)) &&
            (!(i_mode & S_IWOTH))) {
#ifdef FS_TRACE
	    kernel_printf("FS_DELETE - %s is read only", pFile);
#endif
            return ERROR_ACCESS_DENIED;
        }


        ExtractPath(pFile, parent);
        ExtractName(pFile, name);
        if ((filp = _open_by_name(sb, parent, OPENMODE_READONLY | DOSmode)) == NULL) {
#ifdef FS_TRACE
            fs_err(FUNC_FS_DELETE, FUNC_OPEN_BY_NAME, -1, THISFILE, __LINE__);
#endif
            return ERROR_PATH_NOT_FOUND;
        } /* end if */
        ino_no = filp->f_inode->i_ino;
        if ((rc = _close(sb, &filp)) != NO_ERROR) {
            fs_err(FUNC_FS_DELETE, FUNC_CLOSE, rc, THISFILE, __LINE__);
            return -1;
        }

        dir = iget(sb, ino_no);
        if ((rc = dir->i_op->unlink (dir, name, strlen(name))) != NO_ERROR) {
            kernel_printf("FS_DELETE(%s) : dir->i_op->unlink() - rc = %d", pFile, rc);
        }
//        iput(dir);
        return map_err(rc);	// rc is a Linux error code (from linux/errno.h)
    } else {
        return ERROR_WRITE_PROTECT;
    }
}



void _FS_ENTRY FS_EXIT(
                       unsigned short uid,
                       unsigned short pid,
                       unsigned short pdb
                      )
{
#ifdef FS_TRACE
    kernel_printf("FS_EXIT( uid = %u pid = %u pdb = %u )", uid, pid, pdb);
#endif
}

_FS_RET _FS_ENTRY FS_FILEATTRIBUTE(
                                   unsigned short         flag,
                                   struct cdfsi   _FS_PTR pcdfsi,
                                   struct cdfsd   _FS_PTR pcdfsd,
                                   char           _FS_PTR pName,
                                   unsigned short         iCurDirEnd,
                                   unsigned short _FS_PTR pAttr
                                  )
{
    int     rc;
    struct super_block *sb;
    pfile   p_file;
    char    component[CCHMAXPATH];
    id_t myid;
    UINT32 DOSmode;

    //
    // Process identification (to recognize DOS box requests)
    //
    if ((rc = FSH_QSYSINFO(2, (pchar)&myid, sizeof(id_t))) != NO_ERROR) {
        fs_log("erreur FSH_QSYSINFO() dans FS_FINDFIRST");
        return rc;
    }
    if (myid.pdb != 0) {
        DOSmode = OPENMODE_DOSBOX;
    } else {
        DOSmode = 0;
    }

    //
    // Gets the superblock from pcdfsi
    //
     sb = getvolume(pcdfsi->cdi_hVPB);

    switch(flag) {
        case FA_RETRIEVE :
#ifdef FS_TRACE
            kernel_printf("FS_FILEATTRIBUTE( %s ) - flag = FA_RETRIEVE", pName);
#endif
            if ((p_file = _open_by_name(sb, pName, OPENMODE_READONLY | DOSmode)) == 0) {
#ifdef FS_TRACE
                fs_err(FUNC_FS_FILEATTRIBUTE, FUNC_OPEN_BY_NAME, ERROR_OPEN_FAILED, FILE_TEST_C, __LINE__);
#endif
                return ERROR_FILE_NOT_FOUND;
            } /* end if */

            ExtractName(pName, component);
            *pAttr = Linux_To_DOS_Attrs(p_file->f_inode, component);

            if ((rc = _close(sb, &p_file)) != NO_ERROR) {
                fs_err(FUNC_FS_CLOSE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
                return rc;
            }

            return NO_ERROR;

        case FA_SET :
#ifdef FS_TRACE
            kernel_printf("FS_FILEATTRIBUTE( %s ) - flag = FA_SET", pName);
#endif
            if (Read_Write) {
		// We should do something here !!
                return NO_ERROR;
            } else {
                return ERROR_WRITE_PROTECT;
            }

        default :
            kernel_printf("FS_FILEATTRIBUTE() Unknown flag %u", flag);
            return ERROR_INVALID_PARAMETER;
    }
    return NO_ERROR;
}


_FS_RET _FS_ENTRY FS_FLUSHBUF(
                              unsigned short hVPB,
                              unsigned short flag
                             )
{

#ifdef FS_TRACE
    kernel_printf("FS_FLUSHBUF(hVPB = %d, flag = %d)", hVPB, flag);
#endif

    //
    // This seems to be called only after FS_SHUTDOWN, for each volume mounted
    //
    return NO_ERROR;
}


_FS_RET _FS_ENTRY FS_FSCTL(
                           union argdat   _FS_PTR pArgdat,
                           unsigned short         iArgType,
                           unsigned short         func,
                           char           _FS_PTR pParm,
                           unsigned short         lenParm,
                           unsigned short _FS_PTR plenParmOut,
                           char           _FS_PTR pData,
                           unsigned short         lenData,
                           unsigned short _FS_PTR plenDataOut
                          )
{
    int rc;
    int i;
    struct super_block *sb;
    struct vpfsi *pvpfsi;
    struct vpfsd *pvpfsd;

    switch(func) {

        /******************* hack **************************************/
        case IFSDBG_FLUSHCACHE :
            if (Read_Write) {
            for (i = 0 ; i < NB_MAX_VOLS ; i++) {
                if (volglobdat.listvol[i].status == VOL_STATUS_MOUNTED) {
                    FSH_GETVOLPARM(volglobdat.listvol[i].hVPB, &pvpfsi, &pvpfsd);
                    sb = ((hvolume *)pvpfsd)->p_volume;

                    if (sb) {
                        kernel_printf("Flushing volume 0x%0X", volglobdat.listvol[i].hVPB);
                        sync_volume(sb);
                    }
                }
            }            
                return NO_ERROR;
            } else {
                return ERROR_NOT_SUPPORTED;
            }
        case IFSDBG_LAZY_WRITE :
            if (Read_Write) {
                for (i = 0 ; i < NB_MAX_VOLS ; i++) {
                    if (volglobdat.listvol[i].status == VOL_STATUS_MOUNTED) {
                        FSH_GETVOLPARM(volglobdat.listvol[i].hVPB, &pvpfsi, &pvpfsd);
                        sb = ((hvolume *)pvpfsd)->p_volume;

                        if (sb) {
                            lazy_write(sb);
                        }
                    }
                }            
                return NO_ERROR;
            } else {
                return ERROR_NOT_SUPPORTED;
            }
        /***************************************************************/


        case IFSDBG_OPEN :
            if (BufOpen == 0) {
                BufOpen = 1;
                if (BufPtr == 0) {
                    if ((rc = FSH_SEMSET(&BufSem)) == ERROR_INTERRUPT) {
                        BufOpen = 0;
                        return rc;
                    } /* end if */
                } /* end if */
                return NO_ERROR;
            } else {
                return ERROR_DEVICE_IN_USE;
            } /* end if */

        case IFSDBG_CLOSE:
            if (BufOpen == 1) {
                BufOpen = 0;
                return NO_ERROR;
            } else {
                return ERROR_DEVICE_NOT_OPEN;
            } /* end if */


        case IFSDBG_READ:
            if (BufOpen == 1) {
                /*************************************************************/
                /*** Waits on the log semaphore                            ***/
                /*************************************************************/
                if ((rc = FSH_SEMWAIT(&BufSem, TO_INFINITE)) == ERROR_INTERRUPT) {
                    BufOpen = 0;
                    return rc;
                } /* end if */
                /*************************************************************/
                /*** verify access to the user buffer                      ***/
                /*************************************************************/
                if ((rc = FSH_PROBEBUF(1, pData, lenData)) != NO_ERROR) {
                    return rc;
                }
                /*************************************************************/
                /*** vefify access to the user lenght to be returned       ***/
                /*************************************************************/
                if ((rc = FSH_PROBEBUF(1, (char _FS_PTR)plenDataOut, sizeof(unsigned int))) != NO_ERROR) {
                    return rc;
                }
                /*************************************************************/
                /*** If no data is present, simply set the semaphore       ***/
                /*************************************************************/
                if (BufPtr == 0) {
                    *plenDataOut = 0;
                    if ((rc = FSH_SEMSET(&BufSem)) == ERROR_INTERRUPT) {
                        BufOpen = 0;
                        return rc;
                    } /* end if */
                    return NO_ERROR;
                } /* end if */

                /*************************************************************/
                /*** If the log data is smaller than the requested amount  ***/
                /*** we copy them all                                      ***/
                /*************************************************************/
                if (BufPtr < lenData) {
                    memcpy(pData, BufMsg, BufPtr + 1);
                    *plenDataOut = BufPtr + 1;
                    BufPtr = 0;
                    if ((rc = FSH_SEMSET(&BufSem)) == ERROR_INTERRUPT) {
                        BufOpen = 0;
                        return rc;
                    } /* end if */
                    return NO_ERROR;
                } /* end if */
                /*************************************************************/
                /*** We set the log semaphore                              ***/
                /*** ext2-os2.exe will wait on it until some more data is  ***/
                /*** present                                               ***/
                /*************************************************************/
                if ((rc = FSH_SEMSET(&BufSem)) == ERROR_INTERRUPT) {
                    BufOpen = 0;
                    return rc;
                } /* end if */

                return NO_ERROR;
            } else {
                return ERROR_DEVICE_NOT_OPEN;
            } /* end if */

	case IFSDBG_SETUID :
	    {
                unsigned short uid = *(unsigned short *)pData;
		setuid(uid);
		return NO_ERROR;
            }
        default:
#ifdef FS_TRACE
           kernel_printf("FS_FSCTL( func = %d ", func);
#endif
           return ERROR_NOT_SUPPORTED;

    } /* end switch */
}

_FS_RET _FS_ENTRY FS_FSINFO(
                            unsigned short         flag,
                            unsigned short         hVPB,
                            char           _FS_PTR pData,
                            unsigned short         cbData,
                            unsigned short         level
                           )
{
    int                rc;
    FSALLOCATE        *pfsil_alloc;
    struct super_block *            sb;
    struct ext2_super_block       *psb;
    struct vpfsi _FS_PTR pvpfsi;
    struct vpfsd _FS_PTR pvpfsd;
    FSINFO            *pvolser;


    /*******************************************************************/
    /*** Traitement des cas suivant flag (INFO_RETRIEVE - INFO_SET)  ***/
    /*******************************************************************/
    switch(flag) {
        /*******************************************************************/
        /*** INFO_RETRIEVE : retrieves file system information           ***/
        /*******************************************************************/
        case INFO_RETREIVE :
            /*******************************************************************/
            /*** Verify write access to the user buffer                      ***/
            /*******************************************************************/
            if ((rc = FSH_PROBEBUF(PB_OPWRITE, pData, cbData)) != NO_ERROR) {
                kernel_printf("FS_FSINFO() - ERROR_BUFFER_OVERFLOW");
                return rc;
            } /* end if */
            /*******************************************************************/
            switch(level) {
                case FSIL_VOLSER :
#ifdef FS_TRACE
                    if ((rc = fs_log("FS_FSINFO : flag = INFO_RETREIVE, level = FSIL_VOLSER")) != NO_ERROR) {
                        return rc;
                    } /* end if */
#endif
                    FSH_GETVOLPARM(hVPB, &pvpfsi, &pvpfsd);
                    if (cbData < sizeof(FSINFO)) {
                        kernel_printf("FS_FSINFO - buffer overflow");
                        return ERROR_BUFFER_OVERFLOW;
                    }
                    pvolser          = (FSINFO *)pData;
                    pvolser->ulVSN   = pvpfsi->vpi_vid;
                    pvolser->vol.cch = (BYTE)strlen(pvpfsi->vpi_text);
                    strcpy(pvolser->vol.szVolLabel, pvpfsi->vpi_text);
                    return NO_ERROR;

                case FSIL_ALLOC :
#ifdef FS_TRACE
                    if ((rc = fs_log("FS_FSINFO : flag = INFO_RETREIVE, level = FSIL_ALLOC")) != NO_ERROR) {
                        return rc;
                    } /* end if */
#endif
                    if (cbData < sizeof(FSALLOCATE)) {
                        kernel_printf("FS_FSINFO - buffer overflow");
                        return ERROR_BUFFER_OVERFLOW;
                    }
                    sb = getvolume(hVPB);
                    psb = (struct ext2_super_block *)sb->u.ext2_sb.s_sbh->b_data;
                    pfsil_alloc = (FSALLOCATE *)pData;
//                  pfsil_alloc->idFileSystem      =  ????? ;
                    pfsil_alloc->cSectorUnit       = sb->sectors_per_block;
                    pfsil_alloc->cUnit             = psb->s_blocks_count;
                    pfsil_alloc->cUnitAvail        = (psb->s_free_blocks_count > psb->s_r_blocks_count ? psb->s_free_blocks_count - psb->s_r_blocks_count : 0);
                    pfsil_alloc->cbSector          = (UINT16)sb->sector_size;
                    return NO_ERROR;

                default :
                    if ((rc = fs_log("FS_FSINFO flag = INFO_RETREIVE, invalid level")) != NO_ERROR) {
                        return rc;
                    } /* end if */
                    return ERROR_INVALID_PARAMETER;
            } /* end switch */
            break;
        /*******************************************************************/

        /*******************************************************************/
        /*** INFO_SET : used to set the volume label and volume serial   ***/
        /***            number. We currently do nothing                  ***/
        /*******************************************************************/
        case INFO_SET :
#ifdef FS_TRACE
            kernel_printf("FS_FSINFO(INFO_SET)");
#endif
            if (Read_Write) {
		return NO_ERROR;
            } else {
                return ERROR_WRITE_PROTECT;
            }
        /*******************************************************************/

        /*******************************************************************/
        /*** Unknown flag                                                ***/
        /*******************************************************************/
        default :
            if ((rc = kernel_printf("FS_FSINFO() Unknown flag %d", flag)) != NO_ERROR) {
                return rc;
            } /* end if */
            return ERROR_INVALID_PARAMETER;
        /*******************************************************************/

    } /* end switch */
    /*******************************************************************/

    return NO_ERROR;
}


_FS_RET _FS_ENTRY FS_IOCTL(
                           struct sffsi   _FS_PTR psffsi,
                           struct sffsd   _FS_PTR psffsd,
                           unsigned short         cat,
                           unsigned short         func,
                           char           _FS_PTR pParm,
                           unsigned short         lenParm,
                           unsigned       _FS_PTR pParmLenInOut,
                           char           _FS_PTR pData,
                           unsigned short         lenData,
                           unsigned       _FS_PTR pDataLenInOut
                          )
{
    int rc;
    struct vpfsi _FS_PTR pvpfsi;
    struct vpfsd _FS_PTR pvpfsd;
#ifdef FS_TRACE
    kernel_printf("FS_IOCTL( cat = 0x%0X, func = 0x%0X )", cat, func);
#endif
    FSH_GETVOLPARM(psffsi->sfi_hVPB, &pvpfsi, &pvpfsd);
    if ((rc = FSH_DEVIOCTL(
                           0,
                           pvpfsi->vpi_hDEV,
                           psffsi->sfi_selfsfn, /* sfn */
                           cat,
                           func,
                           pParm,
                           *pParmLenInOut,
                           pData,
                           *pDataLenInOut
                          )) != NO_ERROR) {
        fs_log("FS_IOCTL() - Erreur FSH_DEVIOCTL");
        return rc;
    }
    return NO_ERROR;
}





_FS_RET _FS_ENTRY FS_MOUNT(
                           unsigned short         flag,
                           struct vpfsi   _FS_PTR pvpfsi,
                           struct vpfsd   _FS_PTR pvpfsd,
                           unsigned short         hVPB,
                           char           _FS_PTR pBoot
                          )
{
    int rc;
    int i;
    struct super_block *sb;
    unsigned short oldhVPB;


    switch(flag) {
        case MOUNT_MOUNT :
             kernel_printf("FS_MOUNT flg = MOUNT_MOUNT hVPB = 0x%04X", hVPB);

             //
             // We zero out pvpfsd ... for safety purposes !
             //
             memset(pvpfsd, 0, sizeof(struct vpfsd));

	     //
             // First make a (small) test to see if it can be a ext2fs volume
             // This is NECESSARY since we could get a FS_MOUNT call for a volume
             // already mounted by another IFS. In this case we MUST know it's not
             // an ext2fs volume BEFORE calling FSH_FINDDUPHVPB (...if not we'll see
             // later a MOUNT_RELEASE for a non ext2fs volume !!  The test case is :
             // put cdfs.ifs after ext2-os2.ifs in CONFIG.SYS, and start WinOS2 file manager)
             //
             if (!Check_Ext2fs_magic(pvpfsi,  hVPB)) {
                 return ERROR_VOLUME_NOT_MOUNTED;
             }

             //
             // The volume serial number is a CRC checksum of the boot sector
             //
             pvpfsi->vpi_vid = updcrc((unsigned char *)pBoot, pvpfsi->vpi_bsize);

             //
             // The volume label is dummy for the moment ("ext2fs_<drive>")  
             // 
             sprintf(pvpfsi->vpi_text, "EXT2FS_%c", pvpfsi->vpi_unit + 'A');

             //
             // Is there another instance of the drive ?
             //     - Yes : update internal volume table and return silently
             //     - No  : continue the mount process
             //
             if ((rc = FSH_FINDDUPHVPB(hVPB, &oldhVPB)) == NO_ERROR) {
                 kernel_printf(" \tFSH_FINDDUPHVPB(0x%0X) - Found dup hVPB 0x%0X", hVPB, oldhVPB);
                 //
                 // We insert the new dup hVPB in the table so that it can be MOUNT_RELEASEd
                 //
                 i = 0;
                 while ((volglobdat.listvol[i].status != VOL_STATUS_FREE) && (i<NB_MAX_VOLS)) {
                     i++;
                 }
                 if (i == NB_MAX_VOLS) {
                     fs_log("ERROR : No more volumes ");
                     return ERROR_VOLUME_NOT_MOUNTED;
                 }
                 volglobdat.listvol[i].hVPB   = hVPB;
                 volglobdat.listvol[i].status = VOL_STATUS_MOUNTED;
                 volglobdat.listvol[i].sb     = 0;

                 
                 //
                 // We update the status of the old hVPB if necessary
                 //
                 i = 0;
                 while ((volglobdat.listvol[i].hVPB != oldhVPB) && (i < NB_MAX_VOLS)) {
                     i++;
                 }
                 if (i == NB_MAX_VOLS) {
                     os2_panic("ERROR : Cannot find dup volume in my internal tables !");
                 }
                 if (volglobdat.listvol[i].status == VOL_STATUS_REMOVED) {
                     kernel_printf("Remounting removed volume");
                     volglobdat.listvol[i].status == VOL_STATUS_MOUNTED;
                 }
                 return NO_ERROR;
             } else {
                 kernel_printf(" \tFSH_FINDDUPHVPB(0x%0X) - No dup hVPB", hVPB);
             }

             i = 0;
             while ((volglobdat.listvol[i].status != VOL_STATUS_FREE) && (i<NB_MAX_VOLS)) {
                 i++;
             }
             if (i == NB_MAX_VOLS) {
                 fs_log("ERROR : No more volumes");
                 return ERROR_VOLUME_NOT_MOUNTED;
             }

             if ((sb = openvolume(pvpfsi, hVPB)) == 0) {
                 return ERROR_VOLUME_NOT_MOUNTED;
             } /* end if */
             ((hvolume *)pvpfsd)->p_volume = sb;


             volglobdat.listvol[i].hVPB   = hVPB;
             volglobdat.listvol[i].status = VOL_STATUS_MOUNTED;
             volglobdat.listvol[i].sb     = sb;



             kernel_printf("Volume characteristics :");
             kernel_printf("\t volume id    : 0x%08X", pvpfsi->vpi_vid);
             kernel_printf("\t hDEV         : 0x%08X", pvpfsi->vpi_hDEV);
             kernel_printf("\t sector size  : %u", pvpfsi->vpi_bsize);
             kernel_printf("\t sector/track : %u", pvpfsi->vpi_trksec);
             kernel_printf("\t heads        : %u", pvpfsi->vpi_nhead);
             kernel_printf("\t tot sectors  : %lu", pvpfsi->vpi_totsec);
             kernel_printf("\t drive (0=A)  : %d", (int)(pvpfsi->vpi_drive));
             kernel_printf("\t unit code    : %d", (int)(pvpfsi->vpi_unit));
             kernel_printf("\t volume label : %s", pvpfsi->vpi_text);
             kernel_printf("\t hVPB         : 0x%08X", hVPB);

             return NO_ERROR;

        case MOUNT_VOL_REMOVED :
             kernel_printf("FS_MOUNT flg = MOUNT_VOL_REMOVED hVPB = 0x%04X", hVPB);
             i = 0;
             while ((volglobdat.listvol[i].hVPB != hVPB) && (i<NB_MAX_VOLS)) {
                 i++;
             }
             if (i == NB_MAX_VOLS) {
                 fs_log("ERROR : volume not found !");
                 return -1;
             }
             if (volglobdat.listvol[i].status != VOL_STATUS_MOUNTED) {
                 fs_log("ERROR : invalid volume status !");
                 return -1;
             }
             volglobdat.listvol[i].status = VOL_STATUS_REMOVED;

             return NO_ERROR;

        case MOUNT_RELEASE :
             kernel_printf("FS_MOUNT flg = MOUNT_RELEASE hVPB = 0x%04X", hVPB);
             i = 0;
             while ((volglobdat.listvol[i].hVPB != hVPB) && (i<NB_MAX_VOLS)) {
                 i++;
             }
             if (i == NB_MAX_VOLS) {
                 fs_log("ERROR : volume not found !");
                 return -1;
             }

             if ((volglobdat.listvol[i].status != VOL_STATUS_MOUNTED) &&
                 (volglobdat.listvol[i].status != VOL_STATUS_REMOVED)) {
                 fs_log("ERROR : invalid volume status !");
                 return -1;
             }
             //
             // Now we unmount the volume
             //
             sb = ((hvolume _FS_PTR)pvpfsd)->p_volume;
             if (sb) {
		if ((volglobdat.listvol[i].status == VOL_STATUS_MOUNTED) &&
                    (Read_Write)) {
			{
			    struct file *f;
                            f = sb->usedhfiles;

 			    while (f) {
				_close(sb, &f);
                                f = sb->usedhfiles;
                            }
                        }
			sync_inodes(sb->s_dev);
			invalidate_inodes(sb->s_dev);
                        ext2_put_super(sb);
                        sync_volume(sb);
                 }
                 volglobdat.listvol[i].status        = VOL_STATUS_FREE; // To protect us against lazy write ....
                 if ((rc = free_volume(&(((hvolume _FS_PTR)pvpfsd)->p_volume))) != NO_ERROR) {
                     kernel_printf("ERROR - free_volume returned %d", rc);
//                   return rc;
                 }
                 memset(pvpfsd, 0, sizeof(struct vpfsd));
             }
             volglobdat.listvol[i].sb            = 0;
             volglobdat.listvol[i].status        = VOL_STATUS_FREE;
             return NO_ERROR;

        case MOUNT_ACCEPT :
             kernel_printf("FS_MOUNT flg = MOUNT_ACCEPT hVPB = 0x%04X", hVPB);
             return ERROR_NOT_SUPPORTED;

        default :
	    kernel_printf("FS_MOUNT() invalid flag %d", flag);
            return ERROR_INVALID_PARAMETER;
    } /* end switch */

}


_FS_RET _FS_ENTRY FS_MOVE(
                          struct cdfsi   _FS_PTR pcdfsi,
                          struct cdfsd   _FS_PTR pcdfsd,
                          char           _FS_PTR pSrc,
                          unsigned short         iSrcCurDirEnd,
                          char           _FS_PTR pDst,
                          unsigned short         iDstCurDirEnd,
                          unsigned short         flags
                         )
{
    int error;
    char *srcPath, *srcName, *dstPath, *dstName;
    struct inode *isrcdir, *idstdir;
    struct file        *file;
    struct super_block *sb;

#ifdef FS_TRACE
    kernel_printf("FS_MOVE(%s -> %s)", pSrc, pDst);
#endif
    sb = getvolume(pcdfsi->cdi_hVPB);

    if (!Read_Write) {
        return ERROR_WRITE_PROTECT;
    }

    if ((srcPath = G_malloc(4 * CCHMAXPATH)) == NULL) {
        kernel_printf("FS_MOVE - G_malloc failed !");
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    srcName = srcPath + CCHMAXPATH;
    dstPath = srcName + CCHMAXPATH;
    dstName = dstPath + CCHMAXPATH;

    ExtractPath(pSrc, srcPath);		// Source path
    ExtractName(pSrc, srcName);		// Source name
    ExtractPath(pDst, dstPath);		// Destination path
    ExtractName(pDst, dstName);		// Destination name

    //
    // Does the destination path exists ? (this is a hack until I write a true dir_namei routine)
    //
    if ((file = _open_by_name(sb, dstPath, OPENMODE_READONLY)) == NULL) {
	kernel_printf("FS_MOVE() - Destination path %s not found", dstPath);
        G_free(srcPath);
        return ERROR_PATH_NOT_FOUND;
    } /* end if */
    idstdir = file->f_inode;
    free_hfile(sb, file);

    //
    // Does the source path exists ? (this is a hack until I write a true dir_namei routine)
    //
    if ((file = _open_by_name(sb, srcPath, OPENMODE_READONLY)) == NULL) {
	kernel_printf("FS_MOVE() - Source path %s not found", srcPath);
	iput(idstdir);
        G_free(srcPath);
        return ERROR_PATH_NOT_FOUND;
    } /* end if */
    isrcdir = file->f_inode;
    free_hfile(sb, file);
    
    //
    // Now we do the move operation
    //
    idstdir->i_count++;
    down(&idstdir->i_sem);
    if (isrcdir->i_op && isrcdir->i_op->rename) {
    error = isrcdir->i_op->rename(isrcdir, srcName, strlen(srcName), 
		                  idstdir, dstName, strlen(dstName));
    } else {
	if (!isrcdir->i_op)
		os2_panic("FS_MOVE - isrcdir->i_op = 0 - shoudn't occur for ext2-os2");
	if (!isrcdir->i_op->rename)
		os2_panic("FS_MOVE - isrcdir->i_op->rename = 0 - shoudn't occur for ext2-os2");

    }
    up(&idstdir->i_sem);
    iput(idstdir);
    G_free(srcPath);
    return map_err(error);

}


_FS_RET _FS_ENTRY FS_NEWSIZE(
                             struct sffsi   _FS_PTR psffsi,
                             struct sffsd   _FS_PTR psffsd,
                             unsigned long          len,
                             unsigned short         IOflag
                            )
{
    struct file  *filp;

#ifdef FS_TRACE
    kernel_printf("FS_NEWSIZE(ino = %lu, sz = %lu)", ((_sffsd *)psffsd)->p_file->f_inode->i_ino, len);
#endif

    if (!Read_Write) {
        kernel_printf("ERROR ! FS_NEWSIZE called and write access not enabled");
        return ERROR_WRITE_PROTECT;
    }

    filp                        = ((_sffsd *)psffsd)->p_file;
    if (!filp) {
        kernel_printf("FS_NEWSIZE() - p_file = NULL");
        return ERROR_INVALID_PARAMETER;
    }

    if (filp->f_inode->i_ino == INODE_DASD) {
        kernel_printf("FS_NEWSIZE() called on a direct access device handle !");
        return ERROR_INVALID_PARAMETER;
    }
    filp->f_inode->i_size       = len;
    psffsi->sfi_size            = len;
    return NO_ERROR;

}



_FS_RET _FS_ENTRY FS_OPENCREATE(
                                struct cdfsi   _FS_PTR pcdfsi,
                                struct cdfsd   _FS_PTR pcdfsd,
                                char           _FS_PTR pName,
                                unsigned short         iCurDirEnd,
                                struct sffsi   _FS_PTR psffsi,
                                struct sffsd   _FS_PTR psffsd,
                                unsigned long          ulOpenMode,
                                unsigned short         openflag,
                                unsigned short _FS_PTR pAction,
                                unsigned short         attr,
                                char           _FS_PTR pEABuf,
                                unsigned short _FS_PTR pfgenFlag
                               )
{
    int rc;
    struct super_block * sb;
    pfile   p_file, dir;
    UINT32 openmode, DOSmode;
    UINT32 accessmode;
    UINT16 newflag, existflag;
    id_t   myid;
    char component[CCHMAXPATH];
    char parent[CCHMAXPATH];
    struct inode *inode;
    struct inode *inode_parent;
    ino_t         ino_no;

#ifdef FS_TRACE
    if ((rc = kernel_printf("FS_OPENCREATE( %s )", pName)) != NO_ERROR) {
        return rc;
    } /* end if */
#endif

    //
    // Process identification (to recognize DOS box requests)
    //
    if ((rc = FSH_QSYSINFO(2, (pchar)&myid, sizeof(id_t))) != NO_ERROR) {
        fs_log("erreur FSH_QSYSINFO() dans FS_FINDFIRST");
        return rc;
    }
    if (myid.pdb != 0) {
        DOSmode = OPENMODE_DOSBOX;
    } else {
        DOSmode = 0;
    }

    //
    // Gets the superblock from psffsi
    //
    sb = getvolume(psffsi->sfi_hVPB);

#ifdef FS_TRACE
    if (ulOpenMode & OPEN_FLAGS_DASD) {
        fs_log("OPEN_FLAGS_DASD");
    }


    if (ulOpenMode & OPEN_FLAGS_WRITE_THROUGH) {
        fs_log("OPEN_FLAGS_WRITE_THROUGH");
    }
    if (ulOpenMode & OPEN_FLAGS_FAIL_ON_ERROR) {
        fs_log("OPEN_FLAGS_FAIL_ON_ERROR");
    }
    if (ulOpenMode & OPEN_FLAGS_NO_CACHE) {
        fs_log("OPEN_FLAGS_NO_CACHE");
    }
    if (ulOpenMode & OPEN_FLAGS_NOINHERIT) {
        fs_log("OPEN_FLAGS_NO_INHERIT");
    }
#endif
    accessmode = ulOpenMode & OPEN_ACCESS_MASK;
    
    if (accessmode == OPEN_ACCESS_READONLY) {
#ifdef FS_TRACE
        fs_log("OPEN_ACCESS_READONLY");
#endif
        openmode = OPENMODE_READONLY;
    }

    if (accessmode == OPEN_ACCESS_WRITEONLY) {
#ifdef FS_TRACE
        fs_log("OPEN_ACCESS_WRITEONLY");
#endif
        openmode = OPENMODE_WRITEONLY;
    }

    if (accessmode == OPEN_ACCESS_READWRITE) {
#ifdef FS_TRACE
        fs_log("OPEN_ACCESS_READWRITE");
#endif
        openmode = OPENMODE_READWRITE;
    }

#ifdef FS_TRACE
    if (accessmode == OPEN_ACCESS_EXECUTE) {
        fs_log("OPEN_ACCESS_EXECUTE");
    }
#endif

    newflag = openflag & OPEN_ACTION_NEW_MASK;

#ifdef FS_TRACE
    if (newflag == OPEN_ACTION_FAIL_IF_NEW) {
        fs_log("OPEN_ACTION_FAIL_IF_NEW");
    }
    if (newflag == OPEN_ACTION_CREATE_IF_NEW) {
        fs_log("OPEN_ACTION_CREATE_IF_NEW");
    }
#endif

    existflag = openflag & OPEN_ACTION_EXIST_MASK;

#ifdef FS_TRACE
    if (existflag == OPEN_ACTION_OPEN_IF_EXISTS) {
        fs_log("OPEN_ACTION_OPEN_IF_EXISTS");
    }
    if (existflag == OPEN_ACTION_FAIL_IF_EXISTS) {
        fs_log("OPEN_ACTION_FAIL_IF_EXISTS");
    }
    if (existflag == OPEN_ACTION_REPLACE_IF_EXISTS) {
        fs_log("OPEN_ACTION_REPLACE_IF_EXISTS");
    }
#endif

    if ((!Read_Write) && 
        ((accessmode == OPEN_ACCESS_READWRITE) || 
         (accessmode == OPEN_ACCESS_WRITEONLY))) {
        fs_log("FS_OPENCREATE() - Write access not enabled");
        return ERROR_WRITE_PROTECT;
    }

    //
    // Direct access open of the whole device
    //
    if (ulOpenMode & OPEN_FLAGS_DASD) {
        kernel_printf("OPEN_FLAGS_DASD");
        if ((p_file = _open_by_inode(sb, INODE_DASD, openmode)) == 0) {
            kernel_printf("FS_OPENCREATE() - couldn't DASD open %s", pName);
            return ERROR_OPEN_FAILED;
        }
        ((_sffsd _FS_PTR)psffsd)->p_file = p_file;
        psffsi->sfi_tstamp   = ST_SCREAT | ST_PCREAT;
        psffsi->sfi_size     = p_file->f_inode->i_size;
        psffsi->sfi_position = p_file->f_pos;
        date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_ctime), &(psffsi->sfi_cdate));
        date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_atime), &(psffsi->sfi_adate));
        date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_mtime), &(psffsi->sfi_mdate));
        return NO_ERROR;

    }

    //
    // Now that we treated the OPEN_FLAGS_DASD special case, lets treat the general case :
    // Try to open the file readonly
    // Success : the file exists
    //     if !S_ISDIR && !S_ISREG => error
    //     if OPEN_ACTION_FAIL_IF_EXISTS set => error
    //     if OPEN_ACTION_OPEN_IF_EXISTS set
    //         <test file attrs>
    //         change the open mode and return OK
    //     if OPEN_ACTION_REPLACE_IF_EXISTS set
    //         OPEN_ACCESS_READONLY or OPEN_ACCESS_EXECUTE set => error
    //         OPEN_ACCESS_READWRITE or OPEN_ACCESS_WRITEONLY set
    //             truncate
    //             change openmode and return
    // Failure : the file does not exist
    //     OPEN_ACCESS_READONLY or OPEN_ACCESS_EXECUTE set => error
    //     OPEN_ACCESS_READWRITE or OPEN_ACCESS_WRITEONLY set
    //         if OPEN_ACTION_CREATE_IF_NEW set
    //             try to create the file
    //             open the file and return
    //         if OPEN_ACTION_FAIL_IF_NEW   set => error

    p_file = _open_by_name(sb, pName, openmode | DOSmode);
    if (p_file) {	// The file exists
        //
        // If it's not a regular file or a directory we cannot open
        // 
        if (!S_ISREG(p_file->f_inode->i_mode)) {
            kernel_printf("Can't FS_OPENCREATE - %s is not a regular file", pName);
            if ((rc =_close(sb, &p_file)) != NO_ERROR) {
                fs_err(FUNC_FS_OPENCREATE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
                return rc;
            }
            return ERROR_ACCESS_DENIED;
        }
        //
        // if OPEN_ACTION_FAIL_IF_EXISTS set => error
        // 
        if (existflag == OPEN_ACTION_FAIL_IF_EXISTS) {
#ifdef FS_TRACE
            fs_log("Can't FS_OPENCREATE() - File exists & OPEN_ACTION_FAIL_IF_EXISTS");
#endif
            if ((rc =_close(sb, &p_file)) != NO_ERROR) {
                fs_err(FUNC_FS_OPENCREATE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
                return rc;
            }
            return ERROR_FILE_EXISTS;
        }

        //
        // if OPEN_ACTION_OPEN_IF_EXISTS : OK
        // 
        if (existflag == OPEN_ACTION_OPEN_IF_EXISTS) {
            *pAction        = FILE_EXISTED;
        }

        //
        // if OPEN_ACTION_REPLACE_IF_EXISTS : truncate
        // 
        if (existflag == OPEN_ACTION_REPLACE_IF_EXISTS) {
            p_file->f_inode->i_op->truncate(p_file->f_inode);
            p_file->f_flags = O_TRUNC;
           *pAction        = FILE_TRUNCATED;
        }
    } else {		// The file doesn't exist
        ExtractPath(pName, parent);
        ExtractName(pName, component);
        //
        // We try to open the parent dir
        //
        if ((dir = _open_by_name(sb, parent, OPENMODE_READONLY | DOSmode)) == 0) {
            kernel_printf("FS_OPENCREATE() - The parent directory %s doesn't seem to exist", parent);
            return ERROR_PATH_NOT_FOUND;
        }

        //
        // The parent dir exists
        //

        //
        // If the file is open for execution : error (it doesn't even exist)
        //
        if (accessmode == OPEN_ACCESS_EXECUTE) {
#ifdef FS_TRACE
            fs_log("Can't FS_OPENCREATE() - File doesn't exist & OPEN_ACCESS_EXECUTE");
#endif
            if ((rc =_close(sb, &dir)) != NO_ERROR) {
                fs_err(FUNC_FS_OPENCREATE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
                return rc;
            }
            return ERROR_FILE_NOT_FOUND;
        }

        //
        // If the file is open for writing or readwrite ...
        //
        if ((accessmode == OPEN_ACCESS_READONLY)  ||
            (accessmode == OPEN_ACCESS_READWRITE) ||
            (accessmode == OPEN_ACCESS_WRITEONLY)) {
            if (newflag == OPEN_ACTION_FAIL_IF_NEW) {
#ifdef FS_TRACE
                fs_log("Can't FS_OPENCREATE() - File doesn't exist &  OPEN_ACTION_FAIL_IF_NEW");
#endif
                if ((rc =_close(sb, &dir)) != NO_ERROR) {
                    fs_err(FUNC_FS_OPENCREATE, FUNC_CLOSE, rc, FILE_TEST_C, __LINE__);
                    return rc;
                }
                return ERROR_OPEN_FAILED;
            }

            if (newflag == OPEN_ACTION_CREATE_IF_NEW) {
                ino_no = dir->f_inode->i_ino;
                if ((rc = _close(sb, &dir)) != NO_ERROR) {
                    fs_err(FUNC_FS_OPENCREATE, FUNC_CLOSE, rc, THISFILE, __LINE__);
                    return rc;
                }
                inode_parent = iget(sb, ino_no);
                inode_parent->i_count++;
                down(&inode_parent->i_sem);
                rc = inode_parent->i_op->create (inode_parent,component, strlen(component), S_IRWXU | S_IFREG , &inode);
                up(&inode_parent->i_sem);
                if (rc) {
                    kernel_printf("Couldn't create %s", pName);
                    iput(inode_parent);
                    return rc;
                }
                ino_no = inode->i_ino;
                iput(inode_parent);
		iput(inode);
                if ((p_file = _open_by_inode(sb, ino_no, openmode)) == 0) {
                    kernel_printf("open_by_inode(%lu) failed in FS_OPENCREATE", ino_no);
                    return ERROR_OPEN_FAILED;
                }
                p_file->f_flags = O_CREAT;
                *pAction        = FILE_CREATED;

            }

        }

    }





    ((_sffsd _FS_PTR)psffsd)->p_file = p_file;
    psffsi->sfi_tstamp   = ST_SCREAT | ST_PCREAT | ST_PREAD | ST_SREAD | ST_PWRITE | ST_SWRITE;
    psffsi->sfi_size     = p_file->f_inode->i_size;
    psffsi->sfi_position = p_file->f_pos;

    date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_ctime), &(psffsi->sfi_cdate));
    date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_atime), &(psffsi->sfi_adate));
    date_unix2dos(p_file->f_inode->i_ctime, &(psffsi->sfi_mtime), &(psffsi->sfi_mdate));

    psffsi->sfi_DOSattr =  (unsigned char)Linux_To_DOS_Attrs(p_file->f_inode, component);
    if ((ulOpenMode & OPEN_FLAGS_WRITE_THROUGH) ||
        (ulOpenMode & OPEN_FLAGS_NO_CACHE)) {
	p_file->f_flags          |= O_SYNC;
	p_file->f_inode->i_flags |= MS_SYNCHRONOUS;
    }
    return NO_ERROR;

}





_FS_RET _FS_ENTRY FS_PROCESSNAME(
                                 pchar pNameBuf
                                )
{
    id_t myid;
    int  rc;
#ifdef FS_TRACE
    kernel_printf("FS_PROCESSNAME( %s )", pNameBuf);
#endif
    //
    // Process identification (to recognize DOS box requests)
    //
    if ((rc = FSH_QSYSINFO(2, (pchar)&myid, sizeof(id_t))) != NO_ERROR) {
        fs_log("erreur FSH_QSYSINFO() dans FS_FINDFIRST");
        return rc;
    }
    if (myid.pdb != 0) {			// DOS Box request => uppercase 
        FSH_UPPERCASE(pNameBuf, CCHMAXPATH, pNameBuf);
    }

    return NO_ERROR;
}


_FS_RET _FS_ENTRY FS_READ(
                          struct sffsi   _FS_PTR psffsi,
                          struct sffsd   _FS_PTR psffsd,
                          char           _FS_PTR pData,
                          unsigned short _FS_PTR pLen,
                          unsigned short         IOflag
                         )
{
    int     rc = NO_ERROR;
    pfile   p_file;
    struct super_block * sb;
    UINT32 BytesRead;

#ifdef FS_TRACE
    kernel_printf("FS_READ(ino = %lu, len = %d",  ((_sffsd _FS_PTR)psffsd)->p_file->f_inode->i_ino, *pLen);
#endif

    //
    // Verify we have write access to the user buffer
    //
    if ((rc = FSH_PROBEBUF(PB_OPWRITE, pData, *pLen)) != NO_ERROR) {
        fs_err(FUNC_FS_READ, FUNC_FSH_PROBEBUF, rc, FILE_TEST_C, __LINE__);
        return rc;
    } /* end if */

    //
    // Gets the superblock from psffsi
    //
    sb = getvolume(psffsi->sfi_hVPB);

    //
    // Gets the file structure from psffsd
    //
    p_file = ((_sffsd _FS_PTR)psffsd)->p_file;
    if (!(p_file = ((_sffsd _FS_PTR)psffsd)->p_file)) {
        kernel_printf("FS_READ() - p_file = NULL");
        return ERROR_INVALID_PARAMETER;
    }

    // 
    // Tests if it is a regular file
    //
    if (!S_ISREG(p_file->f_inode->i_mode)) {
        kernel_printf("Can't FS_READ( %lu ) - Not a regular file", p_file->f_inode->i_ino);
	*pLen = 0;
        return ERROR_ACCESS_DENIED;
    }


    //
    // If the file position in psffsi is not consistent with the
    // one in the file structure in psffsd, something went wrong => panic
    //
    if (psffsi->sfi_position != (UINT32)p_file->f_pos) {
        kernel_printf("FS_READ( %lu ) very weird : psffsi->sfi_position (%ld) != p_file->f_pos (%ld)", p_file->f_inode->i_ino, psffsi->sfi_position, p_file->f_pos);	
        *pLen = 0;
	return ERROR_READ_FAULT;	// Maybe we should FSH_INTERR ?
    }

    rc = VFS_read(p_file, pData, (UINT32)*pLen, &BytesRead);
    *pLen                = (UINT16)BytesRead;
    psffsi->sfi_tstamp   = ST_SREAD | ST_PREAD;
    psffsi->sfi_position = p_file->f_pos;
    return rc;

}


_FS_RET _FS_ENTRY FS_SHUTDOWN(
                              unsigned short usType,
                              unsigned long ulReserved
                             )
{
    int rc;
    int i;
    struct super_block *sb;
    struct vpfsi       *pvpfsi;
    struct vpfsd       *pvpfsd;

    if ((rc = fs_log("FS_SHUTDOWN")) != NO_ERROR) {
        return rc;
    } /* end if */
    
    switch(usType) {
        case SD_BEGIN :
            break;
        case SD_COMPLETE :
            //
	    // Normally at this stage the system should have commited/closed every
            // file handles.
	    //
            for (i = 0 ; i < NB_MAX_VOLS ; i++) {
                if (volglobdat.listvol[i].status == VOL_STATUS_MOUNTED) {
                    FSH_GETVOLPARM(volglobdat.listvol[i].hVPB, &pvpfsi, &pvpfsd);
                    sb = ((hvolume *)pvpfsd)->p_volume;

                    if ((sb) && (Read_Write)) {
                        kernel_printf("Flushing volume 0x%0X", volglobdat.listvol[i].hVPB);
			//
			// Are there any opened files yet (normally no) ?
			// This closes pending search handles (the WPS seems to let pending
			// search handles opened ...)
			//
			{
			    struct file *f;
                            f = sb->usedhfiles;

 			    while (f) {
				kernel_printf("FS_SHUTDOWN - Found opened file %lu", f->f_inode->i_ino);
				_close(sb, &f);
                                f = sb->usedhfiles;
                            }
                        }
			sync_inodes(sb->s_dev);
			invalidate_inodes(sb->s_dev);
                        ext2_put_super(sb);
                        sync_volume(sb);
                    }
                }
            }
            break;

        default :
            return ERROR_INVALID_PARAMETER;
    }
    return NO_ERROR;
}




_FS_RET _FS_ENTRY FS_WRITE(
                           struct sffsi   _FS_PTR psffsi,
                           struct sffsd   _FS_PTR psffsd,
                           char           _FS_PTR pData,
                           unsigned short _FS_PTR pLen,
                           unsigned short         IOflag
                          )
{
    int     rc, err;
    pfile   p_file;
    struct super_block * sb;
    long   BytesWritten;

#ifdef FS_TRACE
    kernel_printf("FS_WRITE( len = %u )", *pLen);
#endif

    if (!Read_Write) {
        kernel_printf("ERROR ! FS_WRITE() called and write access not enabled");
        return ERROR_WRITE_PROTECT;
    }

    //
    // Verify we have read access to the user buffer
    //
    if ((rc = FSH_PROBEBUF(PB_OPREAD, pData, *pLen)) != NO_ERROR) {
        kernel_printf("FS_WRITE : FSH_PROBEBUF() returned %d", rc);
        return rc;
    } /* end if */

    //
    // Gets the superblock from psffsi
    //
    sb = getvolume(psffsi->sfi_hVPB);

    //
    // Gets the file structure from psffsd
    //
    p_file = ((_sffsd _FS_PTR)psffsd)->p_file;
    if (!(p_file = ((_sffsd _FS_PTR)psffsd)->p_file)) {
        kernel_printf("FS_WRITE() - p_file = NULL");
        return ERROR_INVALID_PARAMETER;
    }

    // 
    // Tests if it is a regular file
    //
    if (!S_ISREG(p_file->f_inode->i_mode)) {
        kernel_printf("Can't FS_WRITE( %lu ) - Not a regular file", p_file->f_inode->i_ino);
	*pLen = 0;
        return ERROR_ACCESS_DENIED;
    }

    //
    // If the file position in psffsi is not consistent with the
    // one in the file structure in psffsd, something went wrong => panic
    //
    if (psffsi->sfi_position != (unsigned long)p_file->f_pos) {
        kernel_printf("FS_WRITE( %lu ) very weird : psffsi->sfi_position != p_file->f_pos : %ld %ld", p_file->f_inode->i_ino, psffsi->sfi_position, p_file->f_pos);
        *pLen = 0;
        return ERROR_WRITE_FAULT;	// Maybe we should FSH_INTERR ?
    }

    err   = VFS_write(p_file, pData, (loff_t)*pLen, &BytesWritten);
    *pLen = (unsigned short)BytesWritten;
    psffsi->sfi_tstamp   = ST_SWRITE | ST_PWRITE;
    psffsi->sfi_size     = p_file->f_inode->i_size;
    psffsi->sfi_position = p_file->f_pos;
    return err;

}



[ RETURN TO DIRECTORY ]