LockHelper.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using SMBLibrary.SMB2;
  11. using Utilities;
  12. namespace SMBLibrary.Server.SMB2
  13. {
  14. internal class LockHelper
  15. {
  16. internal static SMB2Command GetLockResponse(LockRequest request, ISMBShare share, SMB2ConnectionState state)
  17. {
  18. SMB2Session session = state.GetSession(request.Header.SessionID);
  19. OpenFileObject openFile = session.GetOpenFileObject(request.FileId);
  20. if (openFile == null)
  21. {
  22. state.LogToServer(Severity.Verbose, "Lock failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile);
  23. return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
  24. }
  25. if (request.Locks.Count == 0)
  26. {
  27. // [MS-SMB2] The lock count MUST be greater than or equal to 1
  28. state.LogToServer(Severity.Verbose, "Lock: Invalid number of locks, must be greater than 0.");
  29. return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
  30. }
  31. // [MS-SMB2] If the flags of the initial SMB2_LOCK_ELEMENT in the Locks array of the request has
  32. // SMB2_LOCKFLAG_UNLOCK set, the server MUST process the lock array as a series of unlocks.
  33. // Otherwise, it MUST process the lock array as a series of lock requests.
  34. bool unlock = request.Locks[0].Unlock;
  35. foreach(LockElement lockElement in request.Locks)
  36. {
  37. if (unlock)
  38. {
  39. if (lockElement.SharedLock || lockElement.ExclusiveLock)
  40. {
  41. state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Lock in a series of unlocks.");
  42. return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
  43. }
  44. }
  45. else
  46. {
  47. if (lockElement.Unlock)
  48. {
  49. state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Unlock in a series of locks.");
  50. return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
  51. }
  52. if (lockElement.SharedLock && lockElement.ExclusiveLock)
  53. {
  54. state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_SHARED_LOCK and SMB2_LOCKFLAG_EXCLUSIVE_LOCK are mutually exclusive.");
  55. return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
  56. }
  57. if (request.Locks.Count > 1 && !lockElement.FailImmediately)
  58. {
  59. state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_FAIL_IMMEDIATELY not set in a series of locks.");
  60. return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
  61. }
  62. }
  63. }
  64. for(int lockIndex = 0; lockIndex < request.Locks.Count; lockIndex++)
  65. {
  66. LockElement lockElement = request.Locks[lockIndex];
  67. if (unlock)
  68. {
  69. NTStatus status = share.FileStore.UnlockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length);
  70. if (status != NTStatus.STATUS_SUCCESS)
  71. {
  72. // [MS-SMB2] If the unlock operation fails, the server MUST fail the operation with the error code received from the object store and stop processing further entries in the Locks array.
  73. state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status);
  74. return new ErrorResponse(request.CommandName, status);
  75. }
  76. state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length);
  77. }
  78. else
  79. {
  80. NTStatus status = share.FileStore.LockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length, lockElement.ExclusiveLock);
  81. if (status != NTStatus.STATUS_SUCCESS)
  82. {
  83. // [MS-SMB2] If the lock operation fails, the server MUST unlock any ranges locked as part of processing the previous entries in the Locks array of this request.
  84. state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status);
  85. for (int index = 0; index < lockIndex; index++)
  86. {
  87. share.FileStore.UnlockFile(openFile.Handle, (long)request.Locks[index].Offset, (long)request.Locks[index].Length);
  88. }
  89. return new ErrorResponse(request.CommandName, status);
  90. }
  91. state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length);
  92. }
  93. }
  94. return new LockResponse();
  95. }
  96. }
  97. }