diff --git a/src/FileCache/FileCacheManager.cs b/src/FileCache/FileCacheManager.cs
index e662b61..5361cc4 100644
--- a/src/FileCache/FileCacheManager.cs
+++ b/src/FileCache/FileCacheManager.cs
@@ -14,8 +14,8 @@ public abstract class FileCacheManager
{
// Magic version for new sysfiles: 3.3.0 packed into a long.
protected const ulong CACHE_VERSION = ( 3 << 16
- + 3 << 8
- + 0 << 0);
+ + 3 << 8
+ + 0 << 0);
public string CacheDir { get; set; }
public string CacheSubFolder { get; set; }
@@ -127,8 +127,21 @@ public virtual FileCachePayload ReadFile(FileCache.PayloadMode mode, string key,
}
try
{
- // TODO: In part of the merge it looked like the policy was force serialized with LocalCacheBinder(), is this intended?
- payload.Policy = Deserialize(policyPath) as SerializableCacheItemPolicy;
+ if (File.Exists(policyPath))
+ {
+ using (FileStream stream = GetStream(policyPath, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ using (BinaryReader reader = new BinaryReader(stream))
+ {
+ // TODO: In part of the merge it looked like the policy was force serialized with LocalCacheBinder(), is this intended?
+ payload.Policy = SerializableCacheItemPolicy.Deserialize(reader, stream.Length);
+ }
+ }
+ }
+ else
+ {
+ payload.Policy = new SerializableCacheItemPolicy();
+ }
}
catch
{
@@ -268,13 +281,13 @@ public virtual long WriteFile(FileCache.PayloadMode mode, string key, FileCacheP
//write the cache policy
using (FileStream stream = GetStream(cachedPolicy, FileMode.Create, FileAccess.Write, FileShare.None))
{
- BinaryFormatter formatter = new BinaryFormatter();
- formatter.Serialize(stream, data.Policy);
-
- // adjust cache size
- cacheSizeDelta += new FileInfo(cachedPolicy).Length;
+ using (BinaryWriter writer = new BinaryWriter(stream))
+ {
+ data.Policy.Serialize(writer);
- stream.Close();
+ // adjust cache size
+ cacheSizeDelta += new FileInfo(cachedPolicy).Length;
+ }
}
return cacheSizeDelta;
@@ -555,7 +568,7 @@ public override Type BindToType(string assemblyName, string typeName)
// Get the type using the typeName and assemblyName
typeToDeserialize = Type.GetType(String.Format("{0}, {1}",
- typeName, assemblyName));
+ typeName, assemblyName));
return typeToDeserialize;
}
diff --git a/src/FileCache/SerializableCacheItemPolicy.cs b/src/FileCache/SerializableCacheItemPolicy.cs
index 1eebbeb..a93f864 100644
--- a/src/FileCache/SerializableCacheItemPolicy.cs
+++ b/src/FileCache/SerializableCacheItemPolicy.cs
@@ -7,11 +7,19 @@ FileCache is distributed under the Apache License 2.0.
Consult "LICENSE.txt" included in this package for the Apache License 2.0.
*/
+using System.IO;
+
+
namespace System.Runtime.Caching
{
[Serializable]
public class SerializableCacheItemPolicy
{
+ // Magic version for new policies: 3.3.0 packed into a long.
+ protected const ulong CACHE_VERSION = ( 3 << 16
+ + 3 << 8
+ + 0 << 0);
+
public DateTimeOffset AbsoluteExpiration { get; set; }
private TimeSpan _slidingExpiration;
@@ -24,7 +32,7 @@ public TimeSpan SlidingExpiration
set
{
_slidingExpiration = value;
- if (_slidingExpiration > new TimeSpan())
+ if (_slidingExpiration > TimeSpan.Zero)
{
AbsoluteExpiration = DateTimeOffset.Now.Add(_slidingExpiration);
}
@@ -45,5 +53,69 @@ public SerializableCacheItemPolicy()
/// The cache key that this particular policy refers to
///
public string Key { get; set; }
+
+ ///
+ /// Serialize this policy to the supplied BinaryWriter.
+ ///
+ /// Older policies use the "[Serializable]" attribute and BinaryFormatter, which is a security risk:
+ /// https://docs.microsoft.com/nl-nl/dotnet/standard/serialization/binaryformatter-security-guide#preferred-alternatives
+ ///
+ /// The newer caches have a 'magic' header we'll look for and serialize their fields manually.
+ ///
+ public void Serialize(BinaryWriter writer)
+ {
+ writer.Write(CACHE_VERSION);
+
+ writer.Write(AbsoluteExpiration.Date.ToBinary());
+ writer.Write(AbsoluteExpiration.Offset.TotalMilliseconds);
+
+ writer.Write(SlidingExpiration.TotalMilliseconds);
+
+ writer.Write(Key);
+ }
+
+ ///
+ /// Deserialize a policy from the supplied BinaryReader.
+ ///
+ /// Older policies use the "[Serializable]" attribute and BinaryFormatter, which is a security risk:
+ /// https://docs.microsoft.com/nl-nl/dotnet/standard/serialization/binaryformatter-security-guide#preferred-alternatives
+ ///
+ /// The newer caches have a 'magic' header we'll look for and deserialize their fields manually.
+ /// If the 'magic' header isn't found, this returns an empty policy.
+ ///
+ public static SerializableCacheItemPolicy Deserialize(BinaryReader reader, long streamLength)
+ {
+ // Can't even check for the magic version number; return empty policy.
+ if (streamLength < sizeof(ulong))
+ return new SerializableCacheItemPolicy();
+
+ try
+ {
+ var version = reader.ReadUInt64();
+ if (version != CACHE_VERSION)
+ // Just return an empty policy if we read an invalid one.
+ // This is likely the older "BinaryFormatter"-serialized policy.
+ return new SerializableCacheItemPolicy();
+
+ return new SerializableCacheItemPolicy {
+ AbsoluteExpiration = new DateTimeOffset(DateTime.FromBinary(reader.ReadInt64()),
+ TimeSpan.FromMilliseconds(reader.ReadDouble())),
+ SlidingExpiration = TimeSpan.FromMilliseconds(reader.ReadDouble()),
+ Key = reader.ReadString(),
+ };
+ }
+ catch (Exception error)
+ {
+ if (error is EndOfStreamException
+ || error is IOException
+ || error is ObjectDisposedException)
+ {
+ // Just return an empty policy if we failed to read.
+ return new SerializableCacheItemPolicy();
+ }
+ // Didn't expect this error type; rethrow it.
+ throw;
+ }
+ }
}
}