#pragma once

typedef struct _FAT_SCAN_CONTEXT *PFAT_SCAN_CONTEXT;
typedef struct _FAT_IO_CONTEXT *PFAT_IO_CONTEXT;
typedef struct _FAT_IRP_CONTEXT *PFAT_IRP_CONTEXT;
typedef PVOID PBCB;

typedef NTSTATUS (*PFAT_OPERATION_HANDLER) (PFAT_IRP_CONTEXT);

/* Node type stuff */
typedef CSHORT FAT_NODE_TYPE;
typedef FAT_NODE_TYPE *PFAT_NODE_TYPE;

#define FatNodeType(Ptr) (*((PFAT_NODE_TYPE)(Ptr)))

/* Node type codes */
#define FAT_NTC_VCB         (CSHORT) '00VF'
#define FAT_NTC_FCB         (CSHORT)   'CF'
#define FAT_NTC_DCB         (CSHORT)   'DF'
#define FAT_NTC_ROOT_DCB    (CSHORT)  'RFD'
#define FAT_NTC_CCB         (CSHORT)  'BCC'
#define FAT_NTC_IRP_CONTEXT (CSHORT)  'PRI'

typedef struct _FAT_GLOBAL_DATA
{
    ERESOURCE Resource;
    PEPROCESS SystemProcess;
    PDRIVER_OBJECT DriverObject;
    PDEVICE_OBJECT DiskDeviceObject;
    LIST_ENTRY VcbListHead;
    NPAGED_LOOKASIDE_LIST NonPagedFcbList;
    NPAGED_LOOKASIDE_LIST ResourceList;
    NPAGED_LOOKASIDE_LIST IrpContextList;
    FAST_IO_DISPATCH FastIoDispatch;
    CACHE_MANAGER_CALLBACKS CacheMgrCallbacks;
    CACHE_MANAGER_CALLBACKS CacheMgrNoopCallbacks;
    BOOLEAN Win31FileSystem;
    BOOLEAN ShutdownStarted;
    /* Jan 1, 1980 System Time */
    LARGE_INTEGER DefaultFileTime;

    /* Queued close */
    ULONG AsyncCloseCount;
    ULONG DelayedCloseCount;
    LIST_ENTRY AsyncCloseList;
    LIST_ENTRY DelayedCloseList;
    PIO_WORKITEM FatCloseItem;

    /* Various flags */
    BOOLEAN AsyncCloseActive;

    /* FullFAT integration */
    FF_IOMAN *Ioman;
    FF_ERROR FF_Error;
} FAT_GLOBAL_DATA;

typedef struct _FAT_PAGE_CONTEXT
{
    PFILE_OBJECT FileObject;
    LARGE_INTEGER EndOfData;
    LARGE_INTEGER Offset;
    LARGE_INTEGER EndOfPage;
    SIZE_T ValidLength;
    PVOID Buffer;
    PBCB Bcb;
    BOOLEAN CanWait;
} FAT_PAGE_CONTEXT, *PFAT_PAGE_CONTEXT;

#define FatPinSetupContext(xContext, xFcb, CanWait)     \
{                                                       \
    (xContext)->FileObject = (xFcb)->StreamFileObject;  \
    (xContext)->EndOfData = (xFcb)->Header.FileSize;    \
    (xContext)->Offset.QuadPart = -1LL;                 \
    (xContext)->Bcb = NULL;                             \
    (xContext)->CanWait = CanWait;                      \
}

#define FatPinCleanupContext(xContext)                  \
    if ((xContext)->Bcb != NULL) {                      \
        CcUnpinData((xContext)->Bcb);                   \
        (xContext)->Bcb = NULL;                         \
    }                                                   \

#define FatPinEndOfPage(xContext, xType) \
    Add2Ptr((xContext)->Buffer, (xContext)->ValidLength, xType)

#define FatPinIsLastPage(xContext) \
    ((xContext)->ValidLength != PAGE_SIZE)

#define IRPCONTEXT_CANWAIT          0x0001
#define IRPCONTEXT_PENDINGRETURNED  0x0002
#define IRPCONTEXT_STACK_IO_CONTEXT 0x0004
#define IRPCONTEXT_WRITETHROUGH		0x0008
#define IRPCONTEXT_TOPLEVEL			0x0010

typedef struct _FAT_IRP_CONTEXT
{
    /*  Type and size of this record (must be FAT_NTC_IRP_CONTEXT) */
    FAT_NODE_TYPE NodeTypeCode;
    CSHORT NodeByteSize;

    PIRP Irp;
    PDEVICE_OBJECT DeviceObject;
    UCHAR MajorFunction;
    UCHAR MinorFunction;
    PFILE_OBJECT FileObject;
    ULONG Flags;
    struct _VCB *Vcb;
    ULONG PinCount;
    FAT_PAGE_CONTEXT Page;
    struct _FAT_IO_CONTEXT *FatIoContext;
    WORK_QUEUE_ITEM WorkQueueItem;
    PFAT_OPERATION_HANDLER QueuedOperationHandler;
    PIO_STACK_LOCATION Stack;
    KEVENT Event;
} FAT_IRP_CONTEXT;

typedef struct _FAT_IO_CONTEXT
{
    PMDL ZeroMdl;
    PIRP Irp;
    LONG RunCount;
    SIZE_T Length;
    LONGLONG Offset;
    PFILE_OBJECT FileObject;

    union
    {
        struct
        {
            PERESOURCE Resource;
            PERESOURCE PagingIoResource;
            ERESOURCE_THREAD ResourceThreadId;
        } Async;
        KEVENT SyncEvent;
    } Wait;
    PIRP AssociatedIrp[0];
} FAT_IO_CONTEXT;

typedef ULONG (*PFAT_SCANFAT_FOR_CONTINOUS_RUN_ROUTINE) (PFAT_PAGE_CONTEXT, PULONG, BOOLEAN);
typedef ULONG (*PFAT_SETFAT_CONTINOUS_RUN_ROUTINE) (PFAT_PAGE_CONTEXT, ULONG, ULONG, BOOLEAN);
typedef ULONG (*PFAT_SCANFAT_FOR_VALUE_RUN_ROUTINE) (PFAT_PAGE_CONTEXT, PULONG, ULONG, BOOLEAN);
typedef ULONG (*PFAT_SETFAT_VALUE_RUN_ROUTINE) (PFAT_PAGE_CONTEXT, ULONG, ULONG, ULONG, BOOLEAN);

typedef struct _FAT_METHODS {
    PFAT_SCANFAT_FOR_CONTINOUS_RUN_ROUTINE ScanContinousRun;
    PFAT_SETFAT_CONTINOUS_RUN_ROUTINE SetContinousRun;
    PFAT_SCANFAT_FOR_VALUE_RUN_ROUTINE ScanValueRun;
    PFAT_SETFAT_VALUE_RUN_ROUTINE SetValueRun;
} FAT_METHODS, *PFAT_METHODS;

#define VCB_STATE_FLAG_LOCKED               0x001
#define VCB_STATE_FLAG_DIRTY                0x002
#define VCB_STATE_MOUNTED_DIRTY             0x004
#define VCB_STATE_CREATE_IN_PROGRESS        0x008
#define VCB_STATE_FLAG_CLOSE_IN_PROGRESS    0x010
#define VCB_STATE_FLAG_DELETED_FCB          0x020
#define VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS 0x040
#define VCB_STATE_FLAG_DEFERRED_FLUSH       0x080
#define VCB_STATE_FLAG_WRITE_PROTECTED      0x100

typedef enum _VCB_CONDITION
{
    VcbGood,
    VcbNotMounted,
    VcbBad
} VCB_CONDITION;

/* Volume Control Block */
typedef struct _VCB
{
    FSRTL_ADVANCED_FCB_HEADER Header;
    FAST_MUTEX HeaderMutex;
    SECTION_OBJECT_POINTERS SectionObjectPointers;

    PFILE_OBJECT StreamFileObject;
    PDEVICE_OBJECT TargetDeviceObject;
    LIST_ENTRY VcbLinks;
    PVPB Vpb;
    ULONG State;
    VCB_CONDITION Condition;
    ERESOURCE Resource;
    struct _CLOSE_CONTEXT *CloseContext;
    LIST_ENTRY AsyncCloseList;
    LIST_ENTRY DelayedCloseList;

    /* Direct volume access */
    SHARE_ACCESS ShareAccess;
    PFILE_OBJECT FileObjectWithVcbLocked;

    /* Notifications support */
    PNOTIFY_SYNC NotifySync;
    LIST_ENTRY NotifyList;

    /*  Volume Characteristics: */
    ULONG SerialNumber;
    BIOS_PARAMETER_BLOCK Bpb;
    ULONG BytesPerClusterLog;
    ULONG BytesPerCluster;
    ULONG SectorsPerFat;
    ULONG DataArea;
    ULONG Sectors;
    ULONG Clusters;
    ULONG IndexDepth;
    ULONG RootDirent;
    ULONG RootDirentSectors;
    LONGLONG BeyondLastClusterInFat;
    FAT_METHODS Methods;

    /*  Root Directory Control block */
    struct _FCB *RootDcb;

    /* Counters */
    ULONG DirectOpenCount;
    ULONG OpenFileCount;
    ULONG ReadOnlyCount;
    ULONG InternalOpenCount;
    ULONG ResidualOpenCount;
    ULONG DirectAccessOpenCount;
    ULONG MediaChangeCount;

    /* FullFAT integration */
    FF_IOMAN *Ioman;
} VCB, *PVCB;

#define VcbToVolumeDeviceObject(xVcb) \
    CONTAINING_RECORD((xVcb), VOLUME_DEVICE_OBJECT, Vcb))

#define VcbToDeviceObject(xVcb) \
    &(VcbToVolumeDeviceObject(xVcb)->DeviceObject)


#define SectorsToBytes(xVcb, xSectrors) \
	((xVcb)->Bpb.BytesPerSector * (xSectrors))

#define BytesToSectors(xVcb, xBytes) \
	((xBytes + (xVcb)->Bpb.BytesPerSector - 1) / (xVcb)->Bpb.BytesPerSector)

#define SectorsToClusters(xVcb, xSectors) \
	((xSectors + (xVcb)->Bpb.SectorsPerCluster - 1) / (xVcb)->Bpb.SectorsPerCluster)

#define VCB_FAT_BITMAP_SIZE				0x10000
#define VcbFatBitmapIndex(xCluster)		((xCluster)/VCB_FAT_BITMAP_SIZE)

/* Volume Device Object */
typedef struct _VOLUME_DEVICE_OBJECT
{
    DEVICE_OBJECT DeviceObject;
    union {
        FSRTL_COMMON_FCB_HEADER VolumeHeader;
        VCB Vcb; /* Must be the last entry! */
    };
} VOLUME_DEVICE_OBJECT, *PVOLUME_DEVICE_OBJECT;

typedef enum _TYPE_OF_OPEN
{
    UnopenedFileObject,
    UserFileOpen,
    UserDirectoryOpen,
    UserVolumeOpen,
    VirtualVolumeFile,
    DirectoryFile,
    EaFile
} TYPE_OF_OPEN;

//
//  Short name always exists in FAT
//
enum _FCB_NAME_TYPE {
    FcbShortName = 0x0,
    FcbLongName
} FCB_NAME_TYPE;

typedef struct _FCB_NAME_LINK {
    struct _FCB *Fcb;
    RTL_SPLAY_LINKS Links;
    union
    {
        OEM_STRING Ansi;
        UNICODE_STRING String;
    } Name;
    BOOLEAN IsDosName;
} FCB_NAME_LINK, *PFCB_NAME_LINK;

typedef enum _FCB_CONDITION
{
    FcbGood,
    FcbBad,
    FcbNeedsToBeVerified
} FCB_CONDITION;

#define FCB_STATE_HAS_NAMES         0x01
#define FCB_STATE_HAS_UNICODE_NAME  0x02
#define FCB_STATE_PAGEFILE          0x04
#define FCB_STATE_DELAY_CLOSE       0x08
#define FCB_STATE_TRUNCATE_ON_CLOSE 0x10
#define FCB_STATE_DELETE_ON_CLOSE   0x20

typedef struct _FCB
{
    FSRTL_ADVANCED_FCB_HEADER Header;
   /*
    * Later we might want to move the next four fields
    * into a separate structureif we decide to split
    * FCB into paged and non paged parts
    * (as it is done in MS implementation
    */
    FAST_MUTEX HeaderMutex; // nonpaged!
    SECTION_OBJECT_POINTERS SectionObjectPointers;
    ERESOURCE Resource; // nonpaged!
    ERESOURCE PagingIoResource; // nonpaged!

    /* First cluster in the fat allocation chain */
    ULONG FirstClusterOfFile;
    /* A list of all FCBs of that DCB */
    LIST_ENTRY ParentDcbLinks;
    /* Reference to the Parent Dcb*/
    struct _FCB *ParentFcb;
    /* Pointer to a Vcb */
    PVCB Vcb;
    /* Fcb state */
    ULONG State;
    /* Fcb condition */
    FCB_CONDITION Condition;
    /* Share access */
    SHARE_ACCESS ShareAccess;
    ULONG FirstCluster;
    /* Links into FCB Tree */
    FCB_NAME_LINK ShortName;
    FCB_NAME_LINK LongName;
    /* Buffer for the short name */
    CHAR ShortNameBuffer[0xc];
    /* Full file name */
    UNICODE_STRING FullFileName;
    /* Long name with exact case */
    UNICODE_STRING ExactCaseLongName;
    /* Hint for the filename length */
    ULONG FileNameLength;
    /* A copy of fat attribute byte */
    UCHAR DirentFatFlags;
    /* File basic info */
    FILE_BASIC_INFORMATION BasicInfo;
    /* FullFAT file handle */
    FF_FILE *FatHandle;
    /* The file has outstanding async writes */
    ULONG OutstandingAsyncWrites;
    /* The outstanding async writes sync event */
    PKEVENT OutstandingAsyncEvent;
    /* Counters */
    ULONG OpenCount;
    ULONG UncleanCount;
    ULONG NonCachedUncleanCount;
    union
    {
        struct
        {
            /* File and Op locks */
            FILE_LOCK Lock;
            OPLOCK Oplock;
        } Fcb;

        struct
        {
            LIST_ENTRY ParentDcbList; /* A list of all FCBs/DCBs opened under this DCB */
            ULONG DirectoryFileOpenCount; /* Sector-based access to the dir */
            PFILE_OBJECT DirectoryFile;
            /* Directory data stream (just handy to have it). */
            //PFILE_OBJECT StreamFileObject;
            /* Names */
            PRTL_SPLAY_LINKS SplayLinksAnsi;
            PRTL_SPLAY_LINKS SplayLinksUnicode;
        } Dcb;
    };
} FCB, *PFCB;

typedef struct _FAT_ENUM_DIRENT_CONTEXT *PFAT_ENUM_DIRENT_CONTEXT;
typedef struct _FAT_ENUM_DIR_CONTEXT *PFAT_ENUM_DIR_CONTEXT;

typedef ULONG (*PFAT_COPY_DIRENT_ROUTINE) (PFAT_ENUM_DIR_CONTEXT, PDIR_ENTRY, PVOID);

typedef struct _FAT_ENUM_DIRENT_CONTEXT
{
    FAT_PAGE_CONTEXT Page;

    /* Copy dirent to dirinfo */
    PFAT_COPY_DIRENT_ROUTINE CopyDirent;
    LONGLONG BytesPerClusterMask;

    /* Info buffer characteristics */
    PVOID Buffer;
    SIZE_T Offset;
    SIZE_T Length;

    /* Criteria */
    PUNICODE_STRING FileName;
    UCHAR CcbFlags;

    /* Lfn buffer/length offsets */
    ULONG LengthOffset;
    ULONG NameOffset;
} FAT_ENUM_DIRENT_CONTEXT;

typedef struct _FAT_FIND_DIRENT_CONTEXT
{
    FAT_PAGE_CONTEXT Page;
    UNICODE_STRING ShortName;
    WCHAR ShortNameBuffer[0x18];
    /* Criteria */
    PUNICODE_STRING FileName;
    BOOLEAN Valid8dot3Name;
} FAT_FIND_DIRENT_CONTEXT, *PFAT_FIND_DIRENT_CONTEXT;

typedef struct _CLOSE_CONTEXT
{
    LIST_ENTRY GlobalLinks;
    LIST_ENTRY VcbLinks;

    PVCB Vcb;
    PFCB Fcb;
    TYPE_OF_OPEN TypeOfOpen;
    BOOLEAN Free;
} CLOSE_CONTEXT, *PCLOSE_CONTEXT;

typedef struct _CCB
{
    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;

    LARGE_INTEGER CurrentByteOffset;
    ULONG Entry;
    UNICODE_STRING SearchPattern;
    UCHAR Flags;
    CLOSE_CONTEXT CloseContext;
} CCB, *PCCB;

typedef enum _FILE_TIME_INDEX
{
    FileCreationTime = 0,
    FileLastAccessTime,
    FileLastWriteTime,
    FileChangeTime
} FILE_TIME_INDEX;

#define CCB_SEARCH_RETURN_SINGLE_ENTRY      0x01
#define CCB_SEARCH_PATTERN_LEGAL_8DOT3      0x02
#define CCB_SEARCH_PATTERN_HAS_WILD_CARD    0x04
#define CCB_DASD_IO                         0x08
#define CCB_READ_ONLY                       0x10
#define CCB_DELETE_ON_CLOSE                 0x20
#define CCB_COMPLETE_DISMOUNT               0x40
#define CCB_CLOSE_CONTEXT                   0x80

extern FAT_GLOBAL_DATA FatGlobalData;
