NotifyChangeHelper.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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 SMBLibrary.SMB1;
  10. using Utilities;
  11. namespace SMBLibrary.Server.SMB1
  12. {
  13. internal class NotifyChangeHelper
  14. {
  15. internal static void ProcessNTTransactNotifyChangeRequest(SMB1Header header, uint maxParameterCount, NTTransactNotifyChangeRequest subcommand, ISMBShare share, SMB1ConnectionState state)
  16. {
  17. SMB1Session session = state.GetSession(header.UID);
  18. OpenFileObject openFile = session.GetOpenFileObject(subcommand.FID);
  19. SMB1AsyncContext context = state.CreateAsyncContext(header.UID, header.TID, header.PID, header.MID, subcommand.FID, state);
  20. // We wish to make sure that the 'Monitoring started' will appear before the 'Monitoring completed' in the log
  21. lock (context)
  22. {
  23. header.Status = share.FileStore.NotifyChange(out context.IORequest, openFile.Handle, subcommand.CompletionFilter, subcommand.WatchTree, (int)maxParameterCount, OnNotifyChangeCompleted, context);
  24. if (header.Status == NTStatus.STATUS_PENDING)
  25. {
  26. state.LogToServer(Severity.Verbose, "NotifyChange: Monitoring of '{0}{1}' started. PID: {2}. MID: {3}.", share.Name, openFile.Path, context.PID, context.MID);
  27. }
  28. else if (header.Status == NTStatus.STATUS_NOT_SUPPORTED)
  29. {
  30. // [MS-CIFS] If the server does not support the NT_TRANSACT_NOTIFY_CHANGE subcommand, it can return an
  31. // error response with STATUS_NOT_IMPLEMENTED [..] in response to an NT_TRANSACT_NOTIFY_CHANGE Request.
  32. header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
  33. }
  34. }
  35. }
  36. private static void OnNotifyChangeCompleted(NTStatus status, byte[] buffer, object context)
  37. {
  38. SMB1AsyncContext asyncContext = (SMB1AsyncContext)context;
  39. // Wait until the 'Monitoring started' will be written to the log
  40. lock (asyncContext)
  41. {
  42. SMB1ConnectionState connection = asyncContext.Connection;
  43. connection.RemoveAsyncContext(asyncContext);
  44. SMB1Session session = connection.GetSession(asyncContext.UID);
  45. if (session != null)
  46. {
  47. OpenFileObject openFile = session.GetOpenFileObject(asyncContext.FileID);
  48. if (openFile != null)
  49. {
  50. connection.LogToServer(Severity.Verbose, "NotifyChange: Monitoring of '{0}{1}' completed. NTStatus: {2}. PID: {3}. MID: {4}.", openFile.ShareName, openFile.Path, status, asyncContext.PID, asyncContext.MID);
  51. }
  52. SMB1Header header = new SMB1Header();
  53. header.Command = CommandName.SMB_COM_NT_TRANSACT;
  54. header.Status = status;
  55. header.Flags = HeaderFlags.CaseInsensitive | HeaderFlags.CanonicalizedPaths | HeaderFlags.Reply;
  56. // [MS-CIFS] SMB_FLAGS2_LONG_NAMES SHOULD be set to 1 when the negotiated dialect is NT LANMAN.
  57. // [MS-CIFS] SMB_FLAGS2_UNICODE SHOULD be set to 1 when the negotiated dialect is NT LANMAN.
  58. // [MS-CIFS] The Windows NT Server implementation of NT_TRANSACT_NOTIFY_CHANGE always returns the names of changed files in Unicode format.
  59. header.Flags2 = HeaderFlags2.LongNamesAllowed | HeaderFlags2.NTStatusCode | HeaderFlags2.Unicode;
  60. header.UID = asyncContext.UID;
  61. header.TID = asyncContext.TID;
  62. header.PID = asyncContext.PID;
  63. header.MID = asyncContext.MID;
  64. if (status == NTStatus.STATUS_SUCCESS)
  65. {
  66. NTTransactNotifyChangeResponse notifyChangeResponse = new NTTransactNotifyChangeResponse();
  67. notifyChangeResponse.FileNotifyInformationBytes = buffer;
  68. byte[] responseSetup = notifyChangeResponse.GetSetup();
  69. byte[] responseParameters = notifyChangeResponse.GetParameters(false);
  70. byte[] responseData = notifyChangeResponse.GetData();
  71. List<SMB1Command> responseList = NTTransactHelper.GetNTTransactResponse(responseSetup, responseParameters, responseData, asyncContext.Connection.MaxBufferSize);
  72. if (responseList.Count == 1)
  73. {
  74. SMB1Message reply = new SMB1Message();
  75. reply.Header = header;
  76. reply.Commands.Add(responseList[0]);
  77. SMBServer.EnqueueMessage(asyncContext.Connection, reply);
  78. }
  79. else
  80. {
  81. // [MS-CIFS] In the event that the number of changes exceeds [..] the maximum size of the NT_Trans_Parameter block in
  82. // the response [..] the NT Trans subsystem MUST return an error response with a Status value of STATUS_NOTIFY_ENUM_DIR.
  83. header.Status = NTStatus.STATUS_NOTIFY_ENUM_DIR;
  84. ErrorResponse response = new ErrorResponse(CommandName.SMB_COM_NT_TRANSACT);
  85. SMB1Message reply = new SMB1Message();
  86. reply.Header = header;
  87. reply.Commands.Add(response);
  88. SMBServer.EnqueueMessage(asyncContext.Connection, reply);
  89. }
  90. }
  91. else
  92. {
  93. // Windows Server 2008 SP1 Will use ErrorResponse to return any status other than STATUS_SUCCESS (including STATUS_CANCELLED and STATUS_DELETE_PENDING).
  94. //
  95. // [MS-CIFS] In the event that the number of changes exceeds the size of the change notify buffer [..]
  96. // the NT Trans subsystem MUST return an error response with a Status value of STATUS_NOTIFY_ENUM_DIR.
  97. ErrorResponse response = new ErrorResponse(CommandName.SMB_COM_NT_TRANSACT);
  98. SMB1Message reply = new SMB1Message();
  99. reply.Header = header;
  100. reply.Commands.Add(response);
  101. SMBServer.EnqueueMessage(asyncContext.Connection, reply);
  102. }
  103. }
  104. }
  105. }
  106. }
  107. }