mirror of
				https://github.com/winfsp/winfsp.git
				synced 2025-10-31 12:08:41 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			595 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			595 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * @file persistence.cpp
 | |
|  *
 | |
|  * @copyright 2015-2022 Bill Zissimopoulos
 | |
|  */
 | |
| /*
 | |
|  * This file is part of WinFsp.
 | |
|  *
 | |
|  * You can redistribute it and/or modify it under the terms of the GNU
 | |
|  * General Public License version 3 as published by the Free Software
 | |
|  * Foundation.
 | |
|  *
 | |
|  * Licensees holding a valid commercial license may use this software
 | |
|  * in accordance with the commercial license agreement provided in
 | |
|  * conjunction with the software.  The terms and conditions of any such
 | |
|  * commercial license agreement shall govern, supersede, and render
 | |
|  * ineffective any application of the GPLv3 license to this software,
 | |
|  * notwithstanding of any reference thereto in the software or
 | |
|  * associated repository.
 | |
|  */
 | |
| /*
 | |
|  * Airfs is based on Memfs with changes contributed by John Oberschelp.
 | |
|  * The contributed changes are under joint copyright by Bill Zissimopoulos
 | |
|  * and John Oberschelp per the Contributor Agreement found at the
 | |
|  * root of this project.
 | |
|  */
 | |
| /*
 | |
|  * Airfs uses a memory-mapped file per volume to achieve persistence.
 | |
|  * The primary advantage of this is that volume loads and saves are automatic.
 | |
|  * The two primary disadvantages, and our workarounds are:
 | |
|  *   1. We can't use standard containers or memory management,
 | |
|  *      so the below Rubbertree and Storage functions are used instead.
 | |
|  *   2. Each process will map the volume to an arbitrary address,
 | |
|  *      so Where<T> offsets are used in place of pointers.
 | |
|  */
 | |
| 
 | |
| #include "common.h"
 | |
| 
 | |
| SpinLock StorageLock, AirprintLock, SetLock;
 | |
| 
 | |
| int SizeCmp ( void* key,  NODE_ x)
 | |
| {
 | |
|     return *(int32_t*)key - ((int32_t*)x)[-1];
 | |
| }
 | |
| 
 | |
| int BlockCmp ( void* key,  NODE_ x)
 | |
| {
 | |
|     int64_t left  = *(int64_t*)key;
 | |
|     int64_t right = x->FileOffset;
 | |
|     return left == right ? 0 : (left < right ? -1 : 1);
 | |
| }
 | |
| 
 | |
| int CaselessNameCmp ( void* key,  NODE_ x)
 | |
| { 
 | |
|     WCHAR* c1 = (WCHAR*) key;
 | |
|     WCHAR* c2 = x->Name;
 | |
|     return _wcsicmp(c1,c2); 
 | |
| }
 | |
| 
 | |
| int ExactNameCmp ( void* key,  NODE_ x)
 | |
| { 
 | |
|     WCHAR* c1 = (WCHAR*) key;
 | |
|     WCHAR* c2 = x->Name;
 | |
|     return wcscmp(c1,c2); 
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| void Airprint (const char * format, ...)
 | |
| {
 | |
|     AirprintLock.Acquire();
 | |
|     va_list args;
 | |
|     va_start(args, format);
 | |
|     char szBuffer[512];
 | |
|     sprintf_s(szBuffer, 511, "Airfs %5.5f  ----", SystemTime() / 10'000'000.0);
 | |
|     vsnprintf(szBuffer+25, 511-25, format, args);
 | |
|     OutputDebugStringA(szBuffer);
 | |
|     va_end(args);
 | |
|     AirprintLock.Release();
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| //  Rubbertree (because it is flexible!)
 | |
| //  Implements a sorted set of elements, using a binary tree.
 | |
| //  Has a function, Near, that finds nodes at or adjacent to a key.
 | |
| //  Attach, Find, and Near use splay to improve random access times.
 | |
| //  First, Last, Next, and Prev do not, to improve sequential access times.
 | |
| //  Replacing each Where<NODE_> with NODE_ would make this a pointer-based tree.
 | |
| //  In addition to Left, Right, and Parent references, each node has an Equal
 | |
| //  reference that may be used to keep "equivalent" nodes. This is used by
 | |
| //  our memory heap manager's tree of available memory blocks, sorted by size.
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| inline void rotateL(Where<NODE_> &root, NODE_ x)
 | |
| {
 | |
|     NODE_ y = x->R, p = x->P;
 | |
|     if (x->R = y->L) y->L->P = x;
 | |
|     if (!(y->P = p))
 | |
|         root = y;
 | |
|     else
 | |
|     {
 | |
|         if (x == p->L) p->L = y;
 | |
|         else           p->R = y;
 | |
|     }
 | |
|     (y->L = x)->P = y;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| inline void rotateR(Where<NODE_> &root, NODE_ y)
 | |
| {
 | |
|     NODE_ x = y->L, p = y->P;
 | |
|     if (y->L = x->R) x->R->P = y;
 | |
|     if (!(x->P = p))
 | |
|         root = x;
 | |
|     else
 | |
|     {
 | |
|         if (y == p->L) p->L = x;
 | |
|         else           p->R = x;
 | |
|     }
 | |
|     (x->R = y)->P = x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| static void splay(Where<NODE_> &root, NODE_ x)
 | |
| {
 | |
|     while (NODE_ p = x->P)
 | |
|     {
 | |
|         if (!p->P)
 | |
|         {
 | |
|             if (p->L == x) rotateR(root, p);
 | |
|             else           rotateL(root, p);
 | |
|         } 
 | |
|         else
 | |
|         {
 | |
|             if (p == p->P->R)
 | |
|             {
 | |
|                 if (p->R == x) rotateL(root, p->P);
 | |
|                 else           rotateR(root, p);
 | |
|                 rotateL(root, x->P); 
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 if (p->L == x) rotateR(root, p->P);
 | |
|                 else           rotateL(root, p);
 | |
|                 rotateR(root, x->P);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| inline int seek(Where<NODE_> &root, NODE_ &x, void* key, CompareFunction CMP) 
 | |
| {
 | |
|     x = root;
 | |
|     for (;;)
 | |
|     {
 | |
|         int diff = CMP(key, x);
 | |
|         if (diff < 0)
 | |
|         {
 | |
|             if (!x->L) return -1;
 | |
|             x = x->L;
 | |
|         }
 | |
|         else if (diff > 0)
 | |
|         {
 | |
|             if (!x->R) return  1;
 | |
|             x = x->R;
 | |
|         }
 | |
|         else           return  0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| inline NODE_ next(NODE_ x)
 | |
| {
 | |
|     if (x->R) { x = x->R; while (x->L) x = x->L; return x; }
 | |
|     NODE_ p = x->P;
 | |
|     while (p && x == p->R) { x = p; p = p->P; }
 | |
|     return p;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| inline NODE_ prev(NODE_ x)
 | |
| {
 | |
|     if (x->L) { x = x->L; while (x->R) x = x->R; return x; }
 | |
|     NODE_ p = x->P;
 | |
|     while (p && x == p->L) { x = p; p = p->P; }
 | |
|     return p;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NODE_ First(NODE_ x)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     if (x) while (x->L) x = x->L; 
 | |
|     SetLock.Release();
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NODE_ Last(NODE_ x)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     if (x) while (x->R) x = x->R;
 | |
|     SetLock.Release();
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
|     
 | |
| NODE_ Next(NODE_ x)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     x = next(x);
 | |
|     SetLock.Release();
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NODE_ Prev(NODE_ x)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     x = prev(x);
 | |
|     SetLock.Release();
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NODE_ Near(Where<NODE_> &root, void* key, CompareFunction CMP, Neighbor want)
 | |
| {
 | |
|     //  Return a node relative to (just <, <=, ==, >=, or >) a key.
 | |
|     if (!root) return 0;
 | |
|     SetLock.Acquire();
 | |
|     NODE_ x;
 | |
|     int dir = seek(root, x, key, CMP);
 | |
|     if ((dir == 0 && want == GT) || (dir > 0 && want >= GE)) x = next(x);
 | |
|     else
 | |
|     if ((dir == 0 && want == LT) || (dir < 0 && want <= LE)) x = prev(x);
 | |
|     else
 | |
|     if (dir != 0 && want == EQ) x = 0;
 | |
|     if (x) splay(root, x);
 | |
|     SetLock.Release();
 | |
|     return x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NODE_ Find(Where<NODE_> &root, void* key, CompareFunction CMP)
 | |
| {
 | |
|     if (!root) return 0;
 | |
|     SetLock.Acquire();
 | |
|     NODE_ x;
 | |
|     int direction = seek(root, x, key, CMP);
 | |
|     splay(root, x);
 | |
|     SetLock.Release();
 | |
|     return direction?0:x;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void Attach(Where<NODE_> &root, NODE_ x, CompareFunction CMP, void* key)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     if (!root)
 | |
|     {
 | |
|         root = x;
 | |
|         x->P = x->L = x->R = x->E = 0;
 | |
|         SetLock.Release();
 | |
|         return;
 | |
|     }
 | |
|     NODE_ f;
 | |
|     int diff = seek(root, f, key, CMP);
 | |
|     if (!diff)
 | |
|     {
 | |
|         if (x->L = f->L) x->L->P = x;
 | |
|         if (x->R = f->R) x->R->P = x;
 | |
|         NODE_ p = f->P;
 | |
|         if (x->P = p) { if (p->L == f) p->L = x; else p->R = x; }
 | |
|         else root = x;
 | |
|         (x->E = f)->P = x;
 | |
|         f->L = f->R = 0;
 | |
|         splay(root, x);
 | |
|         SetLock.Release();
 | |
|         return;
 | |
|     }
 | |
|     if (diff < 0) f->L = x; else f->R = x;
 | |
|     x->P = f;
 | |
|     x->L = x->R = x->E = 0;
 | |
|     splay(root, x);
 | |
|     SetLock.Release();
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void Detach(Where<NODE_> &root, NODE_ x)
 | |
| {
 | |
|     SetLock.Acquire();
 | |
|     NODE_ e = x->E, p = x->P;
 | |
|     if (p && p->E == x) { if (p->E = e) e->P = p; }
 | |
|     else if (e)
 | |
|     {
 | |
|         if (e->L = x->L) e->L->P = e;
 | |
|         if (e->R = x->R) e->R->P = e;
 | |
|         if (e->P = p) { if (p->L == x) p->L = e; else p->R = e; }
 | |
|         else root = e;
 | |
|     }
 | |
|     else if (!x->L)
 | |
|     {
 | |
|         if (p) { if ( p->L == x) p->L = x->R; else p->R = x->R; }
 | |
|         else root = x->R;
 | |
|         if (x->R) x->R->P = p;
 | |
|     }
 | |
|     else if (!x->R)
 | |
|     {
 | |
|         if (p) { if ( p->L == x) p->L = x->L; else p->R = x->L; }
 | |
|         else root = x->L;
 | |
|         if (x->L) x->L->P = p; 
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         e = x->L;
 | |
|         if (e->R)
 | |
|         {
 | |
|             do { e = e->R; } while (e->R);
 | |
|             if (e->P->R = e->L) e->L->P = e->P;
 | |
|             (e->L = x->L)->P = e;
 | |
|         }
 | |
|         (e->R = x->R)->P = e;
 | |
|         if (e->P = x->P) { if (e->P->L == x) e->P->L = e; else e->P->R = e; }
 | |
|         else root = e;
 | |
|     }
 | |
|     SetLock.Release();
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| //
 | |
| //  Storage Functions for our memory-mapped file-based persistent volumes
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void* StorageAllocate(AIRFS_ Airfs, int64_t RequestedSize)
 | |
| {
 | |
|     if (!RequestedSize) return 0;
 | |
|     if (RequestedSize + sizeof int32_t > MAXIMUM_ALLOCSIZE) return 0;
 | |
| 
 | |
|     StorageLock.Acquire();
 | |
|     int32_t RoundedSize = (int32_t) ROUND_UP(RequestedSize, MINIMUM_ALLOCSIZE - sizeof int32_t);
 | |
|     int32_t SplitableSize = RoundedSize + MINIMUM_ALLOCSIZE;
 | |
| 
 | |
|     //  See if we have a freed node of the size we requested.
 | |
|     NODE_ NewItem = Near(Airfs->Available, &RoundedSize, SizeCmp, GE);
 | |
|     if (NewItem)
 | |
|     {
 | |
|         int32_t FoundSize = ((int32_t*)NewItem)[-1];
 | |
|         if (FoundSize < SplitableSize)
 | |
|         {
 | |
|             Detach(Airfs->Available, NewItem);
 | |
|             Airfs->FreeSize -= FoundSize;
 | |
|             StorageLock.Release();
 | |
|             return NewItem;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //  If not, see if we can downsize a larger freed element.
 | |
|     NewItem = Near(Airfs->Available, &SplitableSize, SizeCmp, GE);
 | |
|     if (NewItem)
 | |
|     {
 | |
|         int32_t FoundSize = ((int32_t*)NewItem)[-1];
 | |
|         Detach(Airfs->Available, NewItem);
 | |
|         Airfs->FreeSize -= FoundSize;
 | |
|         char* Addr = (char*)NewItem + RoundedSize + sizeof int32_t;
 | |
|         NODE_ Remainder = (NODE_) Addr;
 | |
|         int32_t RemainderSize = FoundSize - (RoundedSize + sizeof int32_t);
 | |
|         ((int32_t*)Remainder)[-1] = RemainderSize;
 | |
|         Attach(Airfs->Available, Remainder, SizeCmp, &RemainderSize);
 | |
|         Airfs->FreeSize += RemainderSize;
 | |
|         ((int32_t*)NewItem)[-1] = RoundedSize;
 | |
|         StorageLock.Release();
 | |
|         return NewItem;
 | |
|     }
 | |
| 
 | |
|     //  If not, give up.
 | |
|     StorageLock.Release();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void* StorageReallocate(AIRFS_ Airfs, void* OldAlloc, int64_t RequestedSize)
 | |
| {
 | |
|     if (!OldAlloc)
 | |
|     {
 | |
|         return StorageAllocate(Airfs, RequestedSize);
 | |
|     }
 | |
| 
 | |
|     if (!RequestedSize)
 | |
|     {
 | |
|         StorageFree(Airfs, OldAlloc);
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     int32_t OldSize = ((int32_t*)OldAlloc)[-1];
 | |
|     void* NewAlloc = StorageAllocate(Airfs, RequestedSize);
 | |
|     if (!NewAlloc) return 0;
 | |
|     memcpy(NewAlloc, OldAlloc, min(RequestedSize, OldSize));
 | |
|     StorageFree(Airfs, OldAlloc);
 | |
|     return NewAlloc;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void StorageFree(AIRFS_ Airfs, void* r)
 | |
| {
 | |
|     if (!r) return;
 | |
|     StorageLock.Acquire();
 | |
|     NODE_ release = (NODE_) r;
 | |
|     int32_t Size = ((int32_t*)r)[-1];
 | |
|     Attach(Airfs->Available, release, SizeCmp, &Size);
 | |
|     Airfs->FreeSize += Size;
 | |
|     StorageLock.Release();
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| void StorageAccessFile(StorageFileAccessType Type, NODE_ Node, int64_t AccessOffset, int64_t NumBytes, char* MemoryAddress)
 | |
| {
 | |
|     StorageLock.Acquire();
 | |
| 
 | |
|     NODE_ Block = Near(Node->FileBlocks, &AccessOffset, BlockCmp, LE);
 | |
|     for (;;)
 | |
|     {
 | |
|         int32_t BlockSize   = ((int32_t*)Block)[-1];
 | |
|         int64_t BlockOffset = Block->FileOffset;
 | |
|         int64_t BlockIndex  = AccessOffset - BlockOffset + FILEBLOCK_OVERHEAD;
 | |
|         int64_t BlockNum    = min(BlockSize-BlockIndex, NumBytes);
 | |
| 
 | |
|         switch (Type)
 | |
|         {
 | |
|           case ZERO  : { memset((char*)Block + BlockIndex, 0, BlockNum); break; }
 | |
|           case READ  : { memcpy(MemoryAddress, (char*)Block + BlockIndex, BlockNum); break; }
 | |
|           case WRITE : { memcpy((char*)Block + BlockIndex, MemoryAddress, BlockNum); break; }
 | |
|         }
 | |
|         NumBytes -= BlockNum;
 | |
|         if (!NumBytes) break;
 | |
|         MemoryAddress += BlockNum;
 | |
|         AccessOffset += BlockNum;
 | |
|         Block = Next(Block);
 | |
|     }
 | |
| 
 | |
|     StorageLock.Release();
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NTSTATUS StorageSetFileCapacity(AIRFS_ Airfs, NODE_ Node, int64_t minimumRequiredCapacity)
 | |
| {
 | |
|     StorageLock.Acquire();
 | |
| 
 | |
|     int64_t TargetCapacity = ROUND_UP(minimumRequiredCapacity, ALLOCATION_UNIT);
 | |
|     NODE_   Block = Last(Node->FileBlocks);
 | |
|     int32_t BlockSize = Block ? ((int32_t*)Block)[-1] : 0;
 | |
|     int64_t CurrentCapacity = Block ? Block->FileOffset + BlockSize - FILEBLOCK_OVERHEAD: 0;
 | |
|     int64_t Add = TargetCapacity - CurrentCapacity;
 | |
| 
 | |
|     while (Add > 0)
 | |
|     {
 | |
|         //  Add a block if we can, preferably as large or larger than we need.
 | |
|         Add += FILEBLOCK_OVERHEAD;
 | |
|         Block             = Near(Airfs->Available, &Add, SizeCmp, GE);
 | |
|         if (!Block) Block = Near(Airfs->Available, &Add, SizeCmp, LT);
 | |
|         Add -= FILEBLOCK_OVERHEAD;
 | |
|         if (Block)
 | |
|         {
 | |
|             Detach(Airfs->Available, Block);
 | |
|             BlockSize = ((int32_t*)Block)[-1];
 | |
|             Airfs->FreeSize -= BlockSize;
 | |
|             Block->FileOffset = CurrentCapacity;
 | |
|             Attach(Node->FileBlocks, Block, BlockCmp, &CurrentCapacity);
 | |
|             CurrentCapacity += BlockSize - FILEBLOCK_OVERHEAD;
 | |
|             Add -= BlockSize - FILEBLOCK_OVERHEAD;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         StorageLock.Release();
 | |
|         return STATUS_INSUFFICIENT_RESOURCES;
 | |
|     }
 | |
| 
 | |
|     //  Throw away any trailing blocks that are no longer needed.
 | |
|     while (Add < 0)
 | |
|     {
 | |
|         Block = Last(Node->FileBlocks);
 | |
|         BlockSize = ((int32_t*)Block)[-1];
 | |
|         if (BlockSize - FILEBLOCK_OVERHEAD > -Add) break;
 | |
|         Add += BlockSize - FILEBLOCK_OVERHEAD;
 | |
|         Detach(Node->FileBlocks, Block);
 | |
|         Attach(Airfs->Available, Block, SizeCmp, &BlockSize);
 | |
|         Airfs->FreeSize += BlockSize;
 | |
|     }
 | |
| 
 | |
|     //  Possibly downsize the last block.
 | |
|     if (Add < 0)
 | |
|     {
 | |
|         Block = Last(Node->FileBlocks);
 | |
|         int32_t OldBlockSize = ((int32_t*)Block)[-1];
 | |
|         int32_t NewBlockSize = OldBlockSize - (int32_t) ROUND_DOWN(-Add, MINIMUM_ALLOCSIZE);
 | |
|         if (NewBlockSize < MINIMUM_ALLOCSIZE) NewBlockSize = MINIMUM_ALLOCSIZE;
 | |
|         int32_t RemainderBlockSize = OldBlockSize - NewBlockSize - sizeof int32_t;
 | |
|         if (RemainderBlockSize >= MINIMUM_ALLOCSIZE)  //  i.e. if not too near the end
 | |
|         {
 | |
|             char* Addr = (char*)Block + NewBlockSize + sizeof int32_t;
 | |
|             NODE_ Remainder = (NODE_) Addr;
 | |
|             ((int32_t*)Remainder)[-1] = RemainderBlockSize;
 | |
|             Attach(Airfs->Available, Remainder, SizeCmp, &RemainderBlockSize);
 | |
|             Airfs->FreeSize += RemainderBlockSize;
 | |
|             ((int32_t*)Block)[-1] = NewBlockSize;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     StorageLock.Release();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NTSTATUS StorageStartup(AIRFS_ &Airfs, WCHAR* MapName, WCHAR* StorageFileName, int64_t VolumeLength)
 | |
| {
 | |
|     HANDLE MapFileHandle = INVALID_HANDLE_VALUE;
 | |
|     Airfs = 0;
 | |
| 
 | |
|     //  Open.
 | |
|     if (*StorageFileName)
 | |
|     {
 | |
|         MapFileHandle = CreateFileW(StorageFileName, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, OPEN_ALWAYS, NULL, NULL);
 | |
|         if (MapFileHandle == INVALID_HANDLE_VALUE) return GetLastErrorAsStatus();
 | |
|     }
 | |
| 
 | |
|     //  Map.
 | |
|     HANDLE MapHandle = CreateFileMappingW(MapFileHandle, NULL, PAGE_EXECUTE_READWRITE, VolumeLength>>32, VolumeLength & 0xFFFFFFFF, MapName);
 | |
|     if (!MapHandle) return GetLastErrorAsStatus();
 | |
| 
 | |
|     //  Point.
 | |
|     char* MappedAddress = (char*) MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, VolumeLength);
 | |
|     if (!MappedAddress) return GetLastErrorAsStatus();
 | |
| 
 | |
|     //  Keep.
 | |
|     Airfs = (AIRFS_) MappedAddress;
 | |
|     Airfs->MapFileHandle = MapFileHandle;
 | |
|     Airfs->MapHandle = MapHandle;
 | |
|     Airfs->VolumeLength = VolumeLength;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| //--------------------------------------------------------------------
 | |
| 
 | |
| NTSTATUS StorageShutdown(AIRFS_ Airfs)
 | |
| {
 | |
|     BOOL Ok;
 | |
|     NTSTATUS Result = 0;
 | |
|     HANDLE M = Airfs->MapHandle;
 | |
|     HANDLE F = Airfs->MapFileHandle;
 | |
| 
 | |
|     Ok = FlushViewOfFile(Airfs, 0);  if (!Ok && !Result) Result = GetLastErrorAsStatus();
 | |
|     if (F != INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|         Ok = FlushFileBuffers(F);    if (!Ok && !Result) Result = GetLastErrorAsStatus();
 | |
|         //  TODO: Set and write a flag something like Airfs->UpdatesCompleted here?
 | |
|     }
 | |
|     Ok = UnmapViewOfFile(Airfs);     if (!Ok && !Result) Result = GetLastErrorAsStatus();
 | |
|     
 | |
|     if (M)
 | |
|     {
 | |
|         Ok = CloseHandle(M);         if (!Ok && !Result) Result = GetLastErrorAsStatus();
 | |
|     }
 | |
|     if (F != INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|         Ok = CloseHandle(F);         if (!Ok && !Result) Result = GetLastErrorAsStatus();
 | |
|     }
 | |
| 
 | |
|     return Result;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////////
 | |
| //////////////////////////////////////////////////////////////////////
 |