crypt32: Implement file stores.
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index db87193..8aa544b 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -104,6 +104,14 @@
 const void *CRYPT_ReadSerializedElement(const BYTE *pbElement,
  DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType);
 
+/* Writes contexts from the memory store to the file. */
+BOOL CRYPT_WriteSerializedFile(HANDLE file, HCERTSTORE store);
+
+/* Reads contexts serialized in the file into the memory store.  Returns FALSE
+ * if the file is not of the expected format.
+ */
+BOOL CRYPT_ReadSerializedFile(HANDLE file, HCERTSTORE store);
+
 /* Fixes up the the pointers in info, where info is assumed to be a
  * CRYPT_KEY_PROV_INFO, followed by its container name, provider name, and any
  * provider parameters, in a contiguous buffer, but where info's pointers are
diff --git a/dlls/crypt32/serialize.c b/dlls/crypt32/serialize.c
index afcd13f..8d25eaa 100644
--- a/dlls/crypt32/serialize.c
+++ b/dlls/crypt32/serialize.c
@@ -38,13 +38,13 @@
 
 static BOOL CRYPT_SerializeStoreElement(const void *context,
  const BYTE *encodedContext, DWORD cbEncodedContext, DWORD contextPropID,
- PCWINE_CONTEXT_INTERFACE contextInterface, DWORD dwFlags, BYTE *pbElement,
- DWORD *pcbElement)
+ PCWINE_CONTEXT_INTERFACE contextInterface, DWORD dwFlags, BOOL omitHashes,
+ BYTE *pbElement, DWORD *pcbElement)
 {
     BOOL ret;
 
-    TRACE("(%p, %p, %08lx, %p, %p)\n", context, contextInterface, dwFlags,
-     pbElement, pcbElement);
+    TRACE("(%p, %p, %08lx, %d, %p, %p)\n", context, contextInterface, dwFlags,
+     omitHashes, pbElement, pcbElement);
 
     if (context)
     {
@@ -54,7 +54,7 @@
         ret = TRUE;
         do {
             prop = contextInterface->enumProps(context, prop);
-            if (prop)
+            if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
             {
                 DWORD propSize = 0;
 
@@ -84,7 +84,7 @@
             prop = 0;
             do {
                 prop = contextInterface->enumProps(context, prop);
-                if (prop)
+                if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
                 {
                     DWORD propSize = 0;
 
@@ -143,7 +143,7 @@
 {
     return CRYPT_SerializeStoreElement(pCertContext,
      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
-     CERT_CERT_PROP_ID, pCertInterface, dwFlags, pbElement, pcbElement);
+     CERT_CERT_PROP_ID, pCertInterface, dwFlags, FALSE, pbElement, pcbElement);
 }
 
 BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext,
@@ -151,7 +151,7 @@
 {
     return CRYPT_SerializeStoreElement(pCrlContext,
      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
-     CERT_CRL_PROP_ID, pCRLInterface, dwFlags, pbElement, pcbElement);
+     CERT_CRL_PROP_ID, pCRLInterface, dwFlags, FALSE, pbElement, pcbElement);
 }
 
 BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext,
@@ -159,7 +159,7 @@
 {
     return CRYPT_SerializeStoreElement(pCtlContext,
      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
-     CERT_CTL_PROP_ID, pCRLInterface, dwFlags, pbElement, pcbElement);
+     CERT_CTL_PROP_ID, pCTLInterface, dwFlags, FALSE, pbElement, pcbElement);
 }
 
 /* Looks for the property with ID propID in the buffer buf.  Returns a pointer
@@ -215,6 +215,80 @@
     return ret;
 }
 
+static BOOL CRYPT_ReadContextProp(
+ const WINE_CONTEXT_INTERFACE *contextInterface, const void *context,
+ const WINE_CERT_PROP_HEADER *hdr, const BYTE *pbElement, DWORD cbElement)
+{
+    BOOL ret;
+
+    if (cbElement < hdr->cb)
+    {
+        SetLastError(E_INVALIDARG);
+        ret = FALSE;
+    }
+    else if (hdr->unknown != 1)
+    {
+        SetLastError(ERROR_FILE_NOT_FOUND);
+        ret = FALSE;
+    }
+    else if (hdr->propID != CERT_CERT_PROP_ID &&
+     hdr->propID != CERT_CRL_PROP_ID && hdr->propID != CERT_CTL_PROP_ID)
+    {
+        /* Have to create a blob for most types, but not
+         * for all.. arghh.
+         */
+        switch (hdr->propID)
+        {
+        case CERT_AUTO_ENROLL_PROP_ID:
+        case CERT_CTL_USAGE_PROP_ID:
+        case CERT_DESCRIPTION_PROP_ID:
+        case CERT_FRIENDLY_NAME_PROP_ID:
+        case CERT_HASH_PROP_ID:
+        case CERT_KEY_IDENTIFIER_PROP_ID:
+        case CERT_MD5_HASH_PROP_ID:
+        case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
+        case CERT_PUBKEY_ALG_PARA_PROP_ID:
+        case CERT_PVK_FILE_PROP_ID:
+        case CERT_SIGNATURE_HASH_PROP_ID:
+        case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
+        case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
+        case CERT_ENROLLMENT_PROP_ID:
+        case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
+        case CERT_RENEWAL_PROP_ID:
+        {
+            CRYPT_DATA_BLOB blob = { hdr->cb,
+             (LPBYTE)pbElement };
+
+            ret = contextInterface->setProp(context,
+             hdr->propID, 0, &blob);
+            break;
+        }
+        case CERT_DATE_STAMP_PROP_ID:
+            ret = contextInterface->setProp(context,
+             hdr->propID, 0, pbElement);
+            break;
+        case CERT_KEY_PROV_INFO_PROP_ID:
+        {
+            PCRYPT_KEY_PROV_INFO info =
+             (PCRYPT_KEY_PROV_INFO)pbElement;
+
+            CRYPT_FixKeyProvInfoPointers(info);
+            ret = contextInterface->setProp(context,
+             hdr->propID, 0, pbElement);
+            break;
+        }
+        default:
+            ret = FALSE;
+        }
+    }
+    else
+    {
+        /* ignore the context itself */
+        ret = TRUE;
+    }
+    return ret;
+}
+
 const void *CRYPT_ReadSerializedElement(const BYTE *pbElement, DWORD cbElement,
  DWORD dwContextTypeFlags, DWORD *pdwContentType)
 {
@@ -310,73 +384,15 @@
                     TRACE("prop is %ld\n", hdr->propID);
                     cbElement -= sizeof(WINE_CERT_PROP_HEADER);
                     pbElement += sizeof(WINE_CERT_PROP_HEADER);
-                    if (cbElement < hdr->cb)
-                    {
-                        SetLastError(E_INVALIDARG);
-                        ret = FALSE;
-                    }
-                    else if (!hdr->propID)
+                    if (!hdr->propID)
                     {
                         /* Like in CRYPT_findPropID, stop if the propID is zero
                          */
                         noMoreProps = TRUE;
                     }
-                    else if (hdr->unknown != 1)
-                    {
-                        SetLastError(ERROR_FILE_NOT_FOUND);
-                        ret = FALSE;
-                    }
-                    else if (hdr->propID != CERT_CERT_PROP_ID &&
-                     hdr->propID != CERT_CRL_PROP_ID && hdr->propID !=
-                     CERT_CTL_PROP_ID)
-                    {
-                        /* Have to create a blob for most types, but not
-                         * for all.. arghh.
-                         */
-                        switch (hdr->propID)
-                        {
-                        case CERT_AUTO_ENROLL_PROP_ID:
-                        case CERT_CTL_USAGE_PROP_ID:
-                        case CERT_DESCRIPTION_PROP_ID:
-                        case CERT_FRIENDLY_NAME_PROP_ID:
-                        case CERT_HASH_PROP_ID:
-                        case CERT_KEY_IDENTIFIER_PROP_ID:
-                        case CERT_MD5_HASH_PROP_ID:
-                        case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
-                        case CERT_PUBKEY_ALG_PARA_PROP_ID:
-                        case CERT_PVK_FILE_PROP_ID:
-                        case CERT_SIGNATURE_HASH_PROP_ID:
-                        case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
-                        case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
-                        case CERT_ENROLLMENT_PROP_ID:
-                        case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
-                        case CERT_RENEWAL_PROP_ID:
-                        {
-                            CRYPT_DATA_BLOB blob = { hdr->cb,
-                             (LPBYTE)pbElement };
-
-                            ret = contextInterface->setProp(context,
-                             hdr->propID, 0, &blob);
-                            break;
-                        }
-                        case CERT_DATE_STAMP_PROP_ID:
-                            ret = contextInterface->setProp(context,
-                             hdr->propID, 0, pbElement);
-                            break;
-                        case CERT_KEY_PROV_INFO_PROP_ID:
-                        {
-                            PCRYPT_KEY_PROV_INFO info =
-                             (PCRYPT_KEY_PROV_INFO)pbElement;
-
-                            CRYPT_FixKeyProvInfoPointers(info);
-                            ret = contextInterface->setProp(context,
-                             hdr->propID, 0, pbElement);
-                            break;
-                        }
-                        default:
-                            FIXME("prop ID %ld: stub\n", hdr->propID);
-                        }
-                    }
+                    else
+                        ret = CRYPT_ReadContextProp(contextInterface, context,
+                         hdr, pbElement, cbElement);
                     pbElement += hdr->cb;
                     cbElement -= hdr->cb;
                     if (!cbElement)
@@ -404,6 +420,184 @@
     return context;
 }
 
+static const BYTE fileHeader[] = { 0, 0, 0, 0, 'C','E','R','T' };
+
+BOOL CRYPT_ReadSerializedFile(HANDLE file, HCERTSTORE store)
+{
+    BYTE fileHeaderBuf[sizeof(fileHeader)];
+    DWORD read;
+    BOOL ret;
+
+    /* Failure reading is non-critical, we'll leave the store empty */
+    ret = ReadFile(file, fileHeaderBuf, sizeof(fileHeaderBuf), &read, NULL);
+    if (ret)
+    {
+        if (!memcmp(fileHeaderBuf, fileHeader, read))
+        {
+            WINE_CERT_PROP_HEADER propHdr;
+            const void *context = NULL;
+            const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
+            LPBYTE buf = NULL;
+            DWORD bufSize = 0;
+
+            do {
+                ret = ReadFile(file, &propHdr, sizeof(propHdr), &read, NULL);
+                if (ret && read == sizeof(propHdr))
+                {
+                    if (contextInterface && context &&
+                     (propHdr.propID == CERT_CERT_PROP_ID ||
+                     propHdr.propID == CERT_CRL_PROP_ID ||
+                     propHdr.propID == CERT_CTL_PROP_ID))
+                    {
+                        /* We have a new context, so free the existing one */
+                        contextInterface->free(context);
+                    }
+                    if (propHdr.cb > bufSize)
+                    {
+                        /* Not reusing realloc, because the old data aren't
+                         * needed any longer.
+                         */
+                        CryptMemFree(buf);
+                        buf = CryptMemAlloc(propHdr.cb);
+                        bufSize = propHdr.cb;
+                    }
+                    if (buf)
+                    {
+                        ret = ReadFile(file, buf, propHdr.cb, &read, NULL);
+                        if (ret && read == propHdr.cb)
+                        {
+                            if (propHdr.propID == CERT_CERT_PROP_ID)
+                            {
+                                contextInterface = pCertInterface;
+                                ret = contextInterface->addEncodedToStore(store,
+                                 X509_ASN_ENCODING, buf, read,
+                                 CERT_STORE_ADD_NEW, &context);
+                            }
+                            else if (propHdr.propID == CERT_CRL_PROP_ID)
+                            {
+                                contextInterface = pCRLInterface;
+                                ret = contextInterface->addEncodedToStore(store,
+                                 X509_ASN_ENCODING, buf, read,
+                                 CERT_STORE_ADD_NEW, &context);
+                            }
+                            else if (propHdr.propID == CERT_CTL_PROP_ID)
+                            {
+                                contextInterface = pCTLInterface;
+                                ret = contextInterface->addEncodedToStore(store,
+                                 X509_ASN_ENCODING, buf, read,
+                                 CERT_STORE_ADD_NEW, &context);
+                            }
+                            else
+                                ret = CRYPT_ReadContextProp(contextInterface,
+                                 context, &propHdr, buf, read);
+                        }
+                    }
+                    else
+                        ret = FALSE;
+                }
+            } while (ret && read > 0);
+            if (contextInterface && context)
+            {
+                /* Free the last context added */
+                contextInterface->free(context);
+            }
+            CryptMemFree(buf);
+            ret = TRUE;
+        }
+    }
+    else
+        ret = TRUE;
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_SerializeCertNoHash(PCCERT_CONTEXT pCertContext,
+ DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
+{
+    return CRYPT_SerializeStoreElement(pCertContext,
+     pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
+     CERT_CERT_PROP_ID, pCertInterface, dwFlags, TRUE, pbElement, pcbElement);
+}
+
+static BOOL WINAPI CRYPT_SerializeCRLNoHash(PCCRL_CONTEXT pCrlContext,
+ DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
+{
+    return CRYPT_SerializeStoreElement(pCrlContext,
+     pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
+     CERT_CRL_PROP_ID, pCRLInterface, dwFlags, TRUE, pbElement, pcbElement);
+}
+
+static BOOL WINAPI CRYPT_SerializeCTLNoHash(PCCTL_CONTEXT pCtlContext,
+ DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
+{
+    return CRYPT_SerializeStoreElement(pCtlContext,
+     pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
+     CERT_CTL_PROP_ID, pCTLInterface, dwFlags, TRUE, pbElement, pcbElement);
+}
+
+static BOOL CRYPT_SerializeContextsToFile(HANDLE file,
+ const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE store)
+{
+    const void *context = NULL;
+    BOOL ret;
+
+    do {
+        context = contextInterface->enumContextsInStore(store, context);
+        if (context)
+        {
+            DWORD size = 0;
+            LPBYTE buf = NULL;
+
+            ret = contextInterface->serialize(context, 0, NULL, &size);
+            if (size)
+                buf = CryptMemAlloc(size);
+            if (buf)
+            {
+                ret = contextInterface->serialize(context, 0, buf, &size);
+                if (ret)
+                    ret = WriteFile(file, buf, size, &size, NULL);
+            }
+            CryptMemFree(buf);
+        }
+        else
+            ret = TRUE;
+    } while (ret && context != NULL);
+    if (context)
+        contextInterface->free(context);
+    return ret;
+}
+
+BOOL CRYPT_WriteSerializedFile(HANDLE file, HCERTSTORE store)
+{
+    static const BYTE fileTrailer[12] = { 0 };
+    WINE_CONTEXT_INTERFACE interface;
+    BOOL ret;
+    DWORD size;
+
+    SetFilePointer(file, 0, NULL, FILE_BEGIN);
+    ret = WriteFile(file, fileHeader, sizeof(fileHeader), &size, NULL);
+    if (ret)
+    {
+        memcpy(&interface, pCertInterface, sizeof(interface));
+        interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash;
+        ret = CRYPT_SerializeContextsToFile(file, &interface, store);
+    }
+    if (ret)
+    {
+        memcpy(&interface, pCRLInterface, sizeof(interface));
+        interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash;
+        ret = CRYPT_SerializeContextsToFile(file, &interface, store);
+    }
+    if (ret)
+    {
+        memcpy(&interface, pCTLInterface, sizeof(interface));
+        interface.serialize = (SerializeElementFunc)CRYPT_SerializeCTLNoHash;
+        ret = CRYPT_SerializeContextsToFile(file, &interface, store);
+    }
+    if (ret)
+        ret = WriteFile(file, fileTrailer, sizeof(fileTrailer), &size, NULL);
+    return ret;
+}
+
 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
diff --git a/dlls/crypt32/store.c b/dlls/crypt32/store.c
index f1cca0e..0aaa89d 100644
--- a/dlls/crypt32/store.c
+++ b/dlls/crypt32/store.c
@@ -163,6 +163,15 @@
     struct list          crlsToDelete;
 } WINE_REGSTOREINFO, *PWINE_REGSTOREINFO;
 
+typedef struct _WINE_FILESTOREINFO
+{
+    DWORD                dwOpenFlags;
+    HCRYPTPROV           cryptProv;
+    PWINECRYPT_CERTSTORE memStore;
+    HANDLE               file;
+    BOOL                 dirty;
+} WINE_FILESTOREINFO, *PWINE_FILESTOREINFO;
+
 typedef struct _WINE_STORE_LIST_ENTRY
 {
     PWINECRYPT_CERTSTORE store;
@@ -695,21 +704,13 @@
          (const void **)ppStoreContext);
     else
     {
-        if (ps->hdr.dwOpenFlags & CERT_STORE_READONLY_FLAG)
-        {
-            SetLastError(ERROR_ACCESS_DENIED);
-            ret = FALSE;
-        }
-        else
-        {
-            ret = TRUE;
-            if (ps->provWriteCert)
-                ret = ps->provWriteCert(ps->hStoreProv, (PCCERT_CONTEXT)cert,
-                 CERT_STORE_PROV_WRITE_ADD_FLAG);
-            if (ret)
-                ret = ps->memStore->certs.addContext(ps->memStore, cert, NULL,
-                 (const void **)ppStoreContext);
-        }
+        ret = TRUE;
+        if (ps->provWriteCert)
+            ret = ps->provWriteCert(ps->hStoreProv, (PCCERT_CONTEXT)cert,
+             CERT_STORE_PROV_WRITE_ADD_FLAG);
+        if (ret)
+            ret = ps->memStore->certs.addContext(ps->memStore, cert, NULL,
+             (const void **)ppStoreContext);
     }
     /* dirty trick: replace the returned context's hCertStore with
      * store.
@@ -1723,12 +1724,215 @@
     return ret;
 }
 
+static void WINAPI CRYPT_FileCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+
+    TRACE("(%p, %08lx)\n", store, dwFlags);
+    if (store->dirty)
+        CRYPT_WriteSerializedFile(store->file, store->memStore);
+    CertCloseStore(store->memStore, dwFlags);
+    CloseHandle(store->file);
+    CryptMemFree(store);
+}
+
+static BOOL WINAPI CRYPT_FileWriteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT cert, DWORD dwFlags)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+
+    TRACE("(%p, %p, %ld)\n", hCertStore, cert, dwFlags);
+    store->dirty = TRUE;
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_FileDeleteCert(HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pCertContext, DWORD dwFlags)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+
+    TRACE("(%p, %p, %08lx)\n", hCertStore, pCertContext, dwFlags);
+    store->dirty = TRUE;
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_FileWriteCRL(HCERTSTORE hCertStore,
+ PCCRL_CONTEXT crl, DWORD dwFlags)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+
+    TRACE("(%p, %p, %ld)\n", hCertStore, crl, dwFlags);
+    store->dirty = TRUE;
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_FileDeleteCRL(HCERTSTORE hCertStore,
+ PCCRL_CONTEXT pCrlContext, DWORD dwFlags)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+
+    TRACE("(%p, %p, %08lx)\n", hCertStore, pCrlContext, dwFlags);
+    store->dirty = TRUE;
+    return TRUE;
+}
+
+static BOOL WINAPI CRYPT_FileControl(HCERTSTORE hCertStore, DWORD dwFlags,
+ DWORD dwCtrlType, void const *pvCtrlPara)
+{
+    PWINE_FILESTOREINFO store = (PWINE_FILESTOREINFO)hCertStore;
+    BOOL ret;
+
+    TRACE("(%p, %08lx, %ld, %p)\n", hCertStore, dwFlags, dwCtrlType,
+     pvCtrlPara);
+
+    switch (dwCtrlType)
+    {
+    case CERT_STORE_CTRL_RESYNC:
+        CRYPT_MemEmptyStore((PWINE_MEMSTORE)store->memStore);
+        CRYPT_ReadSerializedFile(store->file, store);
+        ret = TRUE;
+        break;
+    case CERT_STORE_CTRL_COMMIT:
+        if (!(store->dwOpenFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
+        {
+            SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+            ret = FALSE;
+        }
+        else if (store->dirty)
+            ret = CRYPT_WriteSerializedFile(store->file, store->memStore);
+        else
+            ret = TRUE;
+        break;
+    default:
+        FIXME("%ld: stub\n", dwCtrlType);
+        ret = FALSE;
+    }
+    return ret;
+}
+
+static void *fileProvFuncs[] = {
+    CRYPT_FileCloseStore,
+    NULL, /* CERT_STORE_PROV_READ_CERT_FUNC */
+    CRYPT_FileWriteCert,
+    CRYPT_FileDeleteCert,
+    NULL, /* CERT_STORE_PROV_SET_CERT_PROPERTY_FUNC */
+    NULL, /* CERT_STORE_PROV_READ_CRL_FUNC */
+    CRYPT_FileWriteCRL,
+    CRYPT_FileDeleteCRL,
+    NULL, /* CERT_STORE_PROV_SET_CRL_PROPERTY_FUNC */
+    NULL, /* CERT_STORE_PROV_READ_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_WRITE_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_DELETE_CTL_FUNC */
+    NULL, /* CERT_STORE_PROV_SET_CTL_PROPERTY_FUNC */
+    CRYPT_FileControl,
+};
+
+static PWINECRYPT_CERTSTORE CRYPT_FileOpenStore(HCRYPTPROV hCryptProv,
+ DWORD dwFlags, const void *pvPara)
+{
+    PWINECRYPT_CERTSTORE store = NULL;
+    HANDLE file = (HANDLE)pvPara;
+
+    TRACE("(%ld, %08lx, %p)\n", hCryptProv, dwFlags, pvPara);
+
+    if (!pvPara)
+    {
+        SetLastError(ERROR_INVALID_HANDLE);
+        return NULL;
+    }
+    if (dwFlags & CERT_STORE_DELETE_FLAG)
+    {
+        SetLastError(E_INVALIDARG);
+        return NULL;
+    }
+    if ((dwFlags & CERT_STORE_READONLY_FLAG) &&
+     (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG))
+    {
+        SetLastError(E_INVALIDARG);
+        return NULL;
+    }
+
+    if (DuplicateHandle(GetCurrentProcess(), (HANDLE)pvPara,
+     GetCurrentProcess(), &file, dwFlags & CERT_STORE_READONLY_FLAG ?
+     GENERIC_READ : GENERIC_READ | GENERIC_WRITE, TRUE, 0))
+    {
+        PWINECRYPT_CERTSTORE memStore;
+
+        memStore = CRYPT_MemOpenStore(hCryptProv, dwFlags, NULL);
+        if (memStore)
+        {
+            if (CRYPT_ReadSerializedFile(file, memStore))
+            {
+                PWINE_FILESTOREINFO info = CryptMemAlloc(
+                 sizeof(WINE_FILESTOREINFO));
+
+                if (info)
+                {
+                    CERT_STORE_PROV_INFO provInfo = { 0 };
+
+                    info->dwOpenFlags = dwFlags;
+                    info->cryptProv = hCryptProv;
+                    info->memStore = memStore;
+                    info->file = file;
+                    info->dirty = FALSE;
+                    provInfo.cbSize = sizeof(provInfo);
+                    provInfo.cStoreProvFunc = sizeof(fileProvFuncs) /
+                     sizeof(fileProvFuncs[0]);
+                    provInfo.rgpvStoreProvFunc = fileProvFuncs;
+                    provInfo.hStoreProv = info;
+                    store = CRYPT_ProvCreateStore(hCryptProv, dwFlags, memStore,
+                     &provInfo);
+                }
+            }
+        }
+    }
+    TRACE("returning %p\n", store);
+    return store;
+}
+
 static PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreW(HCRYPTPROV hCryptProv,
  DWORD dwFlags, const void *pvPara)
 {
-    FIXME("(%ld, %08lx, %s): stub\n", hCryptProv, dwFlags,
-     debugstr_w((LPCWSTR)pvPara));
-    return NULL;
+    HCERTSTORE store = 0;
+    LPCWSTR fileName = (LPCWSTR)pvPara;
+    DWORD access, create;
+    HANDLE file;
+
+    TRACE("(%ld, %08lx, %s)\n", hCryptProv, dwFlags, debugstr_w(fileName));
+
+    if (!fileName)
+    {
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return NULL;
+    }
+    if (!(dwFlags & (CERT_FILE_STORE_COMMIT_ENABLE_FLAG |
+     CERT_STORE_READONLY_FLAG)))
+    {
+        SetLastError(ERROR_FILE_NOT_FOUND);
+        return NULL;
+    }
+
+    access = GENERIC_READ;
+    if (dwFlags & CERT_FILE_STORE_COMMIT_ENABLE_FLAG)
+        access |= GENERIC_WRITE;
+    if (dwFlags & CERT_STORE_CREATE_NEW_FLAG)
+        create = CREATE_NEW;
+    else if (dwFlags & CERT_STORE_OPEN_EXISTING_FLAG)
+        create = OPEN_EXISTING;
+    else
+        create = OPEN_ALWAYS;
+    file = CreateFileW(fileName, access, FILE_SHARE_READ, NULL, create,
+     FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file != INVALID_HANDLE_VALUE)
+    {
+        /* FIXME: need to check whether it's a serialized store; if not, fall
+         * back to a PKCS#7 signed message, then to a single serialized cert.
+         */
+        store = CertOpenStore(CERT_STORE_PROV_FILE, 0, hCryptProv, dwFlags,
+         file);
+        CloseHandle(file);
+    }
+    return (PWINECRYPT_CERTSTORE)store;
 }
 
 static PWINECRYPT_CERTSTORE CRYPT_FileNameOpenStoreA(HCRYPTPROV hCryptProv,
@@ -1788,6 +1992,9 @@
         case (int)CERT_STORE_PROV_MEMORY:
             openFunc = CRYPT_MemOpenStore;
             break;
+        case (int)CERT_STORE_PROV_FILE:
+            openFunc = CRYPT_FileOpenStore;
+            break;
         case (int)CERT_STORE_PROV_REG:
             openFunc = CRYPT_RegOpenStore;
             break;
@@ -1822,6 +2029,8 @@
     }
     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_MEMORY))
         openFunc = CRYPT_MemOpenStore;
+    else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_FILENAME_W))
+        openFunc = CRYPT_FileOpenStore;
     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_SYSTEM))
         openFunc = CRYPT_SysOpenStoreW;
     else if (!strcasecmp(lpszStoreProvider, sz_CERT_STORE_PROV_COLLECTION))
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c
index 1298b29..d0186e2 100644
--- a/dlls/crypt32/tests/store.c
+++ b/dlls/crypt32/tests/store.c
@@ -1003,6 +1003,361 @@
     RegDeleteKeyW(HKEY_CURRENT_USER, BogusPathW);
 }
 
+static const BYTE serializedStoreWithCert[] = {
+ 0x00,0x00,0x00,0x00,0x43,0x45,0x52,0x54,0x20,0x00,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0x7c,0x00,0x00,0x00,0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30,
+ 0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,
+ 0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31,
+ 0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,
+ 0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15,
+ 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,
+ 0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03,0x01,0x00,
+ 0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,
+ 0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00 };
+static const BYTE serializedStoreWithCertAndCRL[] = {
+ 0x00,0x00,0x00,0x00,0x43,0x45,0x52,0x54,0x20,0x00,0x00,0x00,0x01,0x00,0x00,
+ 0x00,0x7c,0x00,0x00,0x00,0x30,0x7a,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30,
+ 0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,
+ 0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31,
+ 0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,
+ 0x30,0x31,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15,
+ 0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,
+ 0x20,0x4c,0x61,0x6e,0x67,0x00,0x30,0x07,0x30,0x02,0x06,0x00,0x03,0x01,0x00,
+ 0xa3,0x16,0x30,0x14,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,
+ 0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x01,0x21,0x00,0x00,0x00,0x01,0x00,
+ 0x00,0x00,0x47,0x00,0x00,0x00,0x30,0x45,0x30,0x2c,0x30,0x02,0x06,0x00,0x30,
+ 0x15,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,
+ 0x6e,0x20,0x4c,0x61,0x6e,0x67,0x00,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,
+ 0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x02,0x06,0x00,0x03,0x11,
+ 0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+
+static void compareFile(LPCWSTR filename, const BYTE *pb, DWORD cb)
+{
+    HANDLE h;
+    BYTE buf[200];
+    BOOL ret;
+    DWORD cbRead = 0, totalRead = 0;
+
+    h = CreateFileW(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+     FILE_ATTRIBUTE_NORMAL, NULL);
+    if (h == INVALID_HANDLE_VALUE)
+        return;
+    do {
+        ret = ReadFile(h, buf, sizeof(buf), &cbRead, NULL);
+        if (ret && cbRead)
+        {
+            ok(totalRead + cbRead <= cb, "Expected total count %ld, see %ld\n",
+             cb, totalRead + cbRead);
+            ok(!memcmp(pb + totalRead, buf, cbRead),
+             "Unexpected data in file\n");
+            totalRead += cbRead;
+        }
+    } while (ret && cbRead);
+    CloseHandle(h);
+}
+
+static void testFileStore(void)
+{
+    static const WCHAR szPrefix[] = { 'c','e','r',0 };
+    static const WCHAR szDot[] = { '.',0 };
+    WCHAR filename[MAX_PATH];
+    HCERTSTORE store;
+    BOOL ret;
+    PCCERT_CONTEXT cert;
+    HANDLE file;
+ 
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0, 0, NULL);
+    ok(!store && GetLastError() == ERROR_INVALID_HANDLE,
+     "Expected ERROR_INVALID_HANDLE, got %08lx\n", GetLastError());
+
+    if (!GetTempFileNameW(szDot, szPrefix, 0, filename))
+       return;
+ 
+    DeleteFileW(filename);
+    file = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file == INVALID_HANDLE_VALUE)
+        return;
+
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0, CERT_STORE_DELETE_FLAG,
+     file);
+    ok(!store && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG | CERT_STORE_READONLY_FLAG, file);
+    ok(!store && GetLastError() == E_INVALIDARG,
+     "Expected E_INVALIDARG, got %08lx\n", GetLastError());
+
+    /* A "read-only" file store.. */
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, file);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        DWORD size;
+
+        ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+         bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
+        /* apparently allows adding certificates.. */
+        ok(ret, "CertAddEncodedCertificateToStore failed: %d\n", ret);
+        /* but not commits.. */
+        ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
+        ok(!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+         "Expected ERROR_CALL_NOT_IMPLEMENTED, got %08lx\n", GetLastError());
+        /* It still has certs in memory.. */
+        cert = CertEnumCertificatesInStore(store, NULL);
+        ok(cert != NULL, "CertEnumCertificatesInStore failed: %08lx\n",
+         GetLastError());
+        CertFreeCertificateContext(cert);
+        /* but the file size is still 0. */
+        size = GetFileSize(file, NULL);
+        ok(size == 0, "Expected size 0, got %ld\n", size);
+        CertCloseStore(store, 0);
+    }
+
+    /* The create new flag is allowed.. */
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_STORE_CREATE_NEW_FLAG, file);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        /* but without the commit enable flag, commits don't happen. */
+        ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+         bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCertificateToStore failed: %d\n", ret);
+        ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
+        ok(!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+         "Expected ERROR_CALL_NOT_IMPLEMENTED, got %08lx\n", GetLastError());
+        CertCloseStore(store, 0);
+    }
+    /* as is the open existing flag. */
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_STORE_OPEN_EXISTING_FLAG, file);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        /* but without the commit enable flag, commits don't happen. */
+        ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+         bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCertificateToStore failed: %d\n", ret);
+        ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
+        ok(!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+         "Expected ERROR_CALL_NOT_IMPLEMENTED, got %08lx\n", GetLastError());
+        CertCloseStore(store, 0);
+    }
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG, file);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        CloseHandle(file);
+        ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+         bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
+         GetLastError());
+        /* with commits enabled, commit is allowed */
+        ret = CertControlStore(store, 0, CERT_STORE_CTRL_COMMIT, NULL);
+        ok(ret, "CertControlStore failed: %d\n", ret);
+        compareFile(filename, serializedStoreWithCert,
+         sizeof(serializedStoreWithCert));
+        CertCloseStore(store, 0);
+    }
+    file = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file == INVALID_HANDLE_VALUE)
+        return;
+    store = CertOpenStore(CERT_STORE_PROV_FILE, 0, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG, file);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        CloseHandle(file);
+        ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING, signedCRL,
+         sizeof(signedCRL), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError());
+        CertCloseStore(store, 0);
+        compareFile(filename, serializedStoreWithCertAndCRL,
+         sizeof(serializedStoreWithCertAndCRL));
+    }
+
+    DeleteFileW(filename);
+}
+
+static void checkFileStoreFailure(LPCWSTR filename, DWORD dwEncodingType,
+ DWORD dwFlags, DWORD expectedError)
+{
+    HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_FILENAME_W,
+     dwEncodingType, 0, dwFlags, filename);
+
+    ok(!store && GetLastError() == expectedError,
+     "Expected %08lx, got %08lx\n", expectedError, GetLastError());
+}
+
+static BOOL initFileFromData(LPCWSTR filename, const BYTE *pb, DWORD cb)
+{
+    HANDLE file = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    BOOL ret;
+
+    if (file != INVALID_HANDLE_VALUE)
+    {
+        DWORD written;
+
+        ret = WriteFile(file, pb, cb, &written, NULL);
+        CloseHandle(file);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+static void testFileNameStore(void)
+{
+    static const WCHAR szPrefix[] = { 'c','e','r',0 };
+    static const WCHAR szDot[] = { '.',0 };
+    WCHAR filename[MAX_PATH];
+    HCERTSTORE store;
+    BOOL ret;
+
+    checkFileStoreFailure(NULL, 0, 0, ERROR_PATH_NOT_FOUND);
+
+    if (!GetTempFileNameW(szDot, szPrefix, 0, filename))
+       return;
+    DeleteFileW(filename);
+
+    /* The two flags are mutually exclusive */
+    checkFileStoreFailure(filename, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG | CERT_STORE_READONLY_FLAG,
+     E_INVALIDARG);
+    /* Without an encoding type, these all fail */
+    checkFileStoreFailure(filename, 0, 0, ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, 0, CERT_STORE_OPEN_EXISTING_FLAG,
+     ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, 0, CERT_STORE_CREATE_NEW_FLAG,
+     ERROR_FILE_NOT_FOUND);
+    /* Without a message encoding type, these still fail */
+    checkFileStoreFailure(filename, X509_ASN_ENCODING, 0, ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, X509_ASN_ENCODING,
+     CERT_STORE_OPEN_EXISTING_FLAG, ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, X509_ASN_ENCODING,
+     CERT_STORE_CREATE_NEW_FLAG, ERROR_FILE_NOT_FOUND);
+    /* Without a cert encoding type, they still fail */
+    checkFileStoreFailure(filename, PKCS_7_ASN_ENCODING, 0,
+     ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, PKCS_7_ASN_ENCODING,
+     CERT_STORE_OPEN_EXISTING_FLAG, ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, PKCS_7_ASN_ENCODING,
+     CERT_STORE_CREATE_NEW_FLAG, ERROR_FILE_NOT_FOUND);
+    /* With both a message and cert encoding type, but without commit enabled,
+     * they still fail
+     */
+    checkFileStoreFailure(filename, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+     ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+     CERT_STORE_OPEN_EXISTING_FLAG, ERROR_FILE_NOT_FOUND);
+    checkFileStoreFailure(filename, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+     CERT_STORE_CREATE_NEW_FLAG, ERROR_FILE_NOT_FOUND);
+
+    /* In all of the following tests, the encoding type seems to be ignored */
+    if (initFileFromData(filename, bigCert, sizeof(bigCert)))
+    {
+        PCCERT_CONTEXT cert;
+        PCCRL_CONTEXT crl;
+
+        store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+         CERT_STORE_READONLY_FLAG, filename);
+        ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+
+        cert = CertEnumCertificatesInStore(store, NULL);
+        todo_wine ok(cert != NULL, "CertEnumCertificatesInStore failed: %08lx\n",
+         GetLastError());
+        cert = CertEnumCertificatesInStore(store, cert);
+        ok(!cert, "Expected only one cert\n");
+        crl = CertEnumCRLsInStore(store, NULL);
+        ok(!crl, "Expected no CRLs\n");
+
+        CertCloseStore(store, 0);
+        DeleteFileW(filename);
+    }
+    if (initFileFromData(filename, serializedStoreWithCert,
+     sizeof(serializedStoreWithCert)))
+    {
+        PCCERT_CONTEXT cert;
+        PCCRL_CONTEXT crl;
+
+        store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+         CERT_STORE_READONLY_FLAG, filename);
+        ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+
+        cert = CertEnumCertificatesInStore(store, NULL);
+        ok(cert != NULL, "CertEnumCertificatesInStore failed: %08lx\n",
+         GetLastError());
+        cert = CertEnumCertificatesInStore(store, cert);
+        ok(!cert, "Expected only one cert\n");
+        crl = CertEnumCRLsInStore(store, NULL);
+        ok(!crl, "Expected no CRLs\n");
+
+        CertCloseStore(store, 0);
+        DeleteFileW(filename);
+    }
+    if (initFileFromData(filename, serializedStoreWithCertAndCRL,
+     sizeof(serializedStoreWithCertAndCRL)))
+    {
+        PCCERT_CONTEXT cert;
+        PCCRL_CONTEXT crl;
+
+        store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+         CERT_STORE_READONLY_FLAG, filename);
+        ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+
+        cert = CertEnumCertificatesInStore(store, NULL);
+        ok(cert != NULL, "CertEnumCertificatesInStore failed: %08lx\n",
+         GetLastError());
+        cert = CertEnumCertificatesInStore(store, cert);
+        ok(!cert, "Expected only one cert\n");
+        crl = CertEnumCRLsInStore(store, NULL);
+        ok(crl != NULL, "CertEnumCRLsInStore failed: %08lx\n", GetLastError());
+        crl = CertEnumCRLsInStore(store, crl);
+        ok(!crl, "Expected only one CRL\n");
+
+        CertCloseStore(store, 0);
+        /* Don't delete it this time, the next test uses it */
+    }
+    /* Now that the file exists, we can open it read-only */
+    store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+     CERT_STORE_READONLY_FLAG, filename);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    CertCloseStore(store, 0);
+    DeleteFileW(filename);
+
+    store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG | CERT_STORE_CREATE_NEW_FLAG, filename);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+         bigCert, sizeof(bigCert), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCertificateToStore failed: %08lx\n",
+         GetLastError());
+        CertCloseStore(store, 0);
+        compareFile(filename, serializedStoreWithCert,
+         sizeof(serializedStoreWithCert));
+    }
+    store = CertOpenStore(CERT_STORE_PROV_FILENAME_W, 0, 0,
+     CERT_FILE_STORE_COMMIT_ENABLE_FLAG, filename);
+    ok(store != NULL, "CertOpenStore failed: %08lx\n", GetLastError());
+    if (store)
+    {
+        ret = CertAddEncodedCRLToStore(store, X509_ASN_ENCODING,
+         signedCRL, sizeof(signedCRL), CERT_STORE_ADD_ALWAYS, NULL);
+        ok(ret, "CertAddEncodedCRLToStore failed: %08lx\n", GetLastError());
+        CertCloseStore(store, 0);
+        compareFile(filename, serializedStoreWithCertAndCRL,
+         sizeof(serializedStoreWithCertAndCRL));
+    }
+}
+
 static void testCertOpenSystemStore(void)
 {
     HCERTSTORE store;
@@ -1185,6 +1540,8 @@
     testRegStore();
     testSystemRegStore();
     testSystemStore();
+    testFileStore();
+    testFileNameStore();
 
     testCertOpenSystemStore();
 
diff --git a/include/wincrypt.h b/include/wincrypt.h
index b34ebfb..6e88249 100644
--- a/include/wincrypt.h
+++ b/include/wincrypt.h
@@ -1617,6 +1617,15 @@
 #define CERT_STORE_OPEN_EXISTING_FLAG               0x00004000
 #define CERT_STORE_READONLY_FLAG                    0x00008000
 
+#define CERT_REGISTRY_STORE_REMOTE_FLAG      0x00010000
+#define CERT_REGISTRY_STORE_SERIALIZED_FLAG  0x00020000
+#define CERT_REGISTRY_STORE_ROAMING_FLAG     0x00040000
+#define CERT_REGISTRY_STORE_MY_IE_DIRTY_FLAG 0x00080000
+#define CERT_REGISTRY_STORE_LM_GPT_FLAG      0x01000000
+#define CERT_REGISTRY_STORE_CLIENT_GPT_FLAG  0x80000000
+
+#define CERT_FILE_STORE_COMMIT_ENABLE_FLAG 0x00010000
+
 /* dwAddDisposition */
 #define CERT_STORE_ADD_NEW                                 1
 #define CERT_STORE_ADD_USE_EXISTING                        2