diff --git a/SMBServer/DirectoryFileSystem.cs b/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs similarity index 78% rename from SMBServer/DirectoryFileSystem.cs rename to SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs index 43f6dac..5d465b2 100644 --- a/SMBServer/DirectoryFileSystem.cs +++ b/SMBServer/DirectoryFileSystem/DirectoryFileSystem.cs @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Tal Aloni . All rights reserved. +/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, @@ -9,12 +9,16 @@ using System.Collections.Generic; using System.IO; using System.Text; using Utilities; +using Microsoft.Win32.SafeHandles; namespace SMBServer { public class DirectoryFileSystem : FileSystem { private DirectoryInfo m_directory; + // If a FileStream is already opened, calling File.SetCreationTime(path, ..) will result in ERROR_SHARING_VIOLATION, + // So we must keep track of the opened file handles and use SetFileTime(handle, ..) + private Dictionary m_openHandles = new Dictionary(); public DirectoryFileSystem(string path) : this(new DirectoryInfo(path)) { @@ -137,7 +141,21 @@ namespace SMBServer { ValidatePath(path); string fullPath = m_directory.FullName + path; - return new FileInfo(fullPath).Open(mode, access, share); + FileStream fileStream = File.Open(fullPath, mode, access, share); + if (!m_openHandles.ContainsKey(fullPath.ToLower())) + { + m_openHandles.Add(fullPath.ToLower(), fileStream.SafeFileHandle); + } + StreamWatcher watcher = new StreamWatcher(fileStream); + watcher.Closed += new EventHandler(Stream_Closed); + return watcher; + } + + private void Stream_Closed(object sender, EventArgs e) + { + StreamWatcher watcher = (StreamWatcher)sender; + FileStream fileStream = (FileStream)watcher.Stream; + m_openHandles.Remove(fileStream.Name.ToLower()); } public override void SetAttributes(string path, bool? isHidden, bool? isReadonly, bool? isArchived) @@ -197,19 +215,40 @@ namespace SMBServer string fullPath = m_directory.FullName + path; if (File.Exists(fullPath)) { - if (creationDT.HasValue) + SafeFileHandle openHandle; + if (m_openHandles.TryGetValue(fullPath.ToLower(), out openHandle)) { - File.SetCreationTime(fullPath, creationDT.Value); - } + if (creationDT.HasValue) + { + Win32Native.SetCreationTime(openHandle, creationDT.Value); + } - if (lastWriteDT.HasValue) - { - File.SetLastWriteTime(fullPath, lastWriteDT.Value); - } + if (lastWriteDT.HasValue) + { + Win32Native.SetLastWriteTime(openHandle, lastWriteDT.Value); + } - if (lastAccessDT.HasValue) + if (lastAccessDT.HasValue) + { + Win32Native.SetLastAccessTime(openHandle, lastAccessDT.Value); + } + } + else { - File.SetLastAccessTime(fullPath, lastAccessDT.Value); + if (creationDT.HasValue) + { + File.SetCreationTime(fullPath, creationDT.Value); + } + + if (lastWriteDT.HasValue) + { + File.SetLastWriteTime(fullPath, lastWriteDT.Value); + } + + if (lastAccessDT.HasValue) + { + File.SetLastAccessTime(fullPath, lastAccessDT.Value); + } } } else if (Directory.Exists(fullPath)) diff --git a/SMBServer/DirectoryFileSystem/StreamWatcher.cs b/SMBServer/DirectoryFileSystem/StreamWatcher.cs new file mode 100644 index 0000000..0ff6452 --- /dev/null +++ b/SMBServer/DirectoryFileSystem/StreamWatcher.cs @@ -0,0 +1,113 @@ +/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.IO; + +namespace SMBServer +{ + /// + /// A wrapper for the stream class that notify when the stream is closed + /// + public class StreamWatcher : Stream + { + private Stream m_stream; + + public event EventHandler Closed; + + public StreamWatcher(Stream stream) + { + m_stream = stream; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return m_stream.Read(buffer, offset, count); + } + + public override void Write(byte[] buffer, int offset, int count) + { + m_stream.Write(buffer, offset, count); + } + + public override void Close() + { + m_stream.Close(); + EventHandler handler = Closed; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + + public override void Flush() + { + m_stream.Flush(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return m_stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + m_stream.SetLength(value); + } + + public override bool CanSeek + { + get + { + return m_stream.CanSeek; + } + } + + public override bool CanRead + { + get + { + return m_stream.CanRead; + } + } + + public override bool CanWrite + { + get + { + return m_stream.CanWrite; + } + } + + public override long Length + { + get + { + return m_stream.Length; + } + } + + public override long Position + { + get + { + return m_stream.Position; + } + set + { + m_stream.Position = value; + } + } + + public Stream Stream + { + get + { + return m_stream; + } + } + } +} diff --git a/SMBServer/DirectoryFileSystem/Win32Native.cs b/SMBServer/DirectoryFileSystem/Win32Native.cs new file mode 100644 index 0000000..dde915a --- /dev/null +++ b/SMBServer/DirectoryFileSystem/Win32Native.cs @@ -0,0 +1,58 @@ +/* Copyright (C) 2014-2017 Tal Aloni . All rights reserved. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU Lesser Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + */ +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace SMBServer +{ + public class Win32Native + { + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetFileTime(SafeFileHandle hFile, ref long lpCreationTime, IntPtr lpLastAccessTime, IntPtr lpLastWriteTime); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetFileTime(SafeFileHandle hFile, IntPtr lpCreationTime, ref long lpLastAccessTime, IntPtr lpLastWriteTime); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetFileTime(SafeFileHandle hFile, IntPtr lpCreationTime, IntPtr lpLastAccessTime, ref long lpLastWriteTime); + + internal static void SetCreationTime(SafeFileHandle hFile, DateTime creationTime) + { + long fileTime = creationTime.ToFileTime(); + bool success = SetFileTime(hFile, ref fileTime, IntPtr.Zero, IntPtr.Zero); + if (!success) + { + uint error = (uint)Marshal.GetLastWin32Error(); + throw new IOException("Win32 error: " + error); + } + } + + internal static void SetLastAccessTime(SafeFileHandle hFile, DateTime lastAccessTime) + { + long fileTime = lastAccessTime.ToFileTime(); + bool success = SetFileTime(hFile, IntPtr.Zero, ref fileTime, IntPtr.Zero); + if (!success) + { + uint error = (uint)Marshal.GetLastWin32Error(); + throw new IOException("Win32 error: " + error); + } + } + + internal static void SetLastWriteTime(SafeFileHandle hFile, DateTime lastWriteTime) + { + long fileTime = lastWriteTime.ToFileTime(); + bool success = SetFileTime(hFile, IntPtr.Zero, IntPtr.Zero, ref fileTime); + if (!success) + { + uint error = (uint)Marshal.GetLastWin32Error(); + throw new IOException("Win32 error: " + error); + } + } + } +} diff --git a/SMBServer/SMBServer.csproj b/SMBServer/SMBServer.csproj index ebd921a..274d8af 100644 --- a/SMBServer/SMBServer.csproj +++ b/SMBServer/SMBServer.csproj @@ -36,7 +36,9 @@ - + + + Form