/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    namcache.h

Abstract:

The NAME_CACHE structure is used to remember the name strings of recent
operations performed at the server so the client can suppress redundant
requests.  For example if an open has recently failed with file not found and
the client app tries it again with an upcased string then we can fail it
immediately with STATUS_OBJECT_NAME_NOT_FOUND without hitting the server.  In
general the algorithm is to put a time window and SMB operation count limit on
the NAME_CACHE entry.  The time window is usually 2 seconds so if NAME_CACHE
entry is more than 2 seconds old the match will fail and the request will go to
the server.  If the request fails again at the server the NAME_CACHE is updated
with another 2 second window.  If the SMB operation count doesn't match then one
or more SMBs have been sent to the server which could make this NAME_CACHE entry
invalid.  So again this operation will get sent to the server.

A NAME_CACHE struct has a mini-rdr portion and an RDBSS portion.  The mini-rdr
portion has a context field (see below), an NTSTATUS field for the result of a
prior server operation on this name entry and a context extension pointer for
some additional mini-rdr specific storage that can be co-allocated with the
NAME_CACHE structure.  See RxNameCacheInitialize().

The SMB operation count is an example of mini-rdr specific state which could be
saved in the context field of MRX_NAME_CACHE.  When the wrapper routine
RxNameCacheCheckEntry() is called it will perform an equality check between the
context field and a supplied parameter as part of finding a match in the name
cache.  When a NAME_CACHE entry is created or updated it is the mini-rdr's job
to supply an appropriate value for this field.

The RDBSS portion of the NAME_CACHE struct contains the name (in a UNICODE
STRING) and the expiration time of the entry.  The MaximumEntries field is used
to limit the number of NAME_CACHE entries created in case a poorly behaved
program were to generate a large number of opens with bad file names and so
consume large quanities of pool.

The NAME_CACHE_CONTROL struct is used to manage a given name cache.  It has
a free list, an active list and a lock used to synchronize updates.

Currently there are name caches for:
   1. OBJECT_NAME_NOT_FOUND - 2 second window, any SMB op sent to the
      server will invalidate it.  This is because you could have the case
      where the client app has a file (foo) open which an app on the server could
      use to signal the creation of a file (bar) on the server.  When the client
      reads file foo and learns that file bar has been created on the
      server then a hit in the name cache which matches bar can't return an
      error.  So this optimization only handles the case of successive file
      opens on the same file which does not yet exist.  Happens in WORD.

Author:

    David Orbits          [davidor]   9-Sep-1996

Revision History:

--*/

#ifndef _NAME_CACHE_DEFINED_
#define _NAME_CACHE_DEFINED_


#ifdef __cplusplus
typedef struct _MRX_NAME_CACHE_ : public MRX_NORMAL_NODE_HEADER {
#else // !__cplusplus
typedef struct _MRX_NAME_CACHE_ {
    MRX_NORMAL_NODE_HEADER;
#endif // __cplusplus

    // !!!! changes above this require realignment with fcb.h

    ULONG Context;                // Operation Count snapshot when entry made
    PVOID ContextExtension;       // Pointer to mini-rdr extension area
    NTSTATUS PriorStatus;         // Saved Status from last attempt at operation

} MRX_NAME_CACHE, *PMRX_NAME_CACHE;


#ifdef __cplusplus
typedef struct _NAME_CACHE : public MRX_NAME_CACHE {
    // I didn't find any use of the spacer in the union below,
    // and the MRX_NAME_CACHE is by definition larger than
    // MRX_NORMAL_NODE_HEADER, so I didn't worry about the union
#else // !__cplusplus
typedef struct _NAME_CACHE {
    //
    // The portion of NAME_CACHE visible to mini redirectors.
    //
    union {
        MRX_NAME_CACHE;
        struct {
           MRX_NORMAL_NODE_HEADER spacer;
        };
    };
#endif // __cplusplus
    //
    // The portion of NAME_CACHE visible to RDBSS.
    //
    LARGE_INTEGER ExpireTime;     // Time when entry expires
    LIST_ENTRY Link;              // Entry on free or active list
    UNICODE_STRING Name;          // Cached name
    ULONG HashValue;              // Hash value of name
    BOOLEAN CaseInsensitive;      // Controls name string compare

} NAME_CACHE, *PNAME_CACHE;


typedef struct _NAME_CACHE_CONTROL_ {

    FAST_MUTEX NameCacheLock;     // Lock to synchronize access to the list
    LIST_ENTRY ActiveList;        // List of active name cache entries
    LIST_ENTRY FreeList;          // Free list of NAME_CACHE structs
    ULONG EntryCount;             // Current number of NAME_CACHE entries allocated
    ULONG MaximumEntries;         // Max number of entries we will allocate
    ULONG MRxNameCacheSize;       // Size of Mini-rdr storage area in entry
    //
    // Stats
    //
    ULONG NumberActivates;        // Number of times cache was updated
    ULONG NumberChecks;           // Number of times cache was checked
    ULONG NumberNameHits;         // Number of times a valid match was returned
    ULONG NumberNetOpsSaved;      // Number of times mini-rdr saved a net op

    ULONG Spare[4];

} NAME_CACHE_CONTROL, *PNAME_CACHE_CONTROL;


//
// Return status for RxNameCacheCheckEntry()
//
typedef enum _RX_NC_CHECK_STATUS {
    RX_NC_SUCCESS = 0,
    RX_NC_TIME_EXPIRED,
    RX_NC_MRXCTX_FAIL
} RX_NC_CHECK_STATUS;



//
// Mini-rdr function to count the number of times the cached state avoided
// a trip to the server.
//
#define RxNameCacheOpSaved(_NCC) (_NCC)->NumberNetOpsSaved += 1



VOID
RxNameCacheInitialize(
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN ULONG MRxNameCacheSize,
    IN ULONG MaximumEntries
    );

PNAME_CACHE
RxNameCacheCreateEntry (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PUNICODE_STRING Name,
    IN BOOLEAN CaseInsensitive
    );

PNAME_CACHE
RxNameCacheFetchEntry (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PUNICODE_STRING Name
    );

RX_NC_CHECK_STATUS
RxNameCacheCheckEntry (
    IN PNAME_CACHE NameCache,
    IN ULONG MRxContext
    );

VOID
RxNameCacheActivateEntry (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PNAME_CACHE NameCache,
    IN ULONG LifeTime,
    IN ULONG MRxContext
    );

VOID
RxNameCacheExpireEntry (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PNAME_CACHE NameCache
    );

VOID
RxNameCacheExpireEntryWithShortName (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PUNICODE_STRING Name
    );

VOID
RxNameCacheFreeEntry (
    IN PNAME_CACHE_CONTROL NameCacheCtl,
    IN PNAME_CACHE NameCache
    );

VOID
RxNameCacheFinalize (
    IN PNAME_CACHE_CONTROL NameCacheCtl
    );

#endif // _NAME_CACHE_DEFINED_
