Skip to content

Commit bbd750c

Browse files
add syncing from network to local
1 parent a055469 commit bbd750c

1 file changed

Lines changed: 224 additions & 73 deletions

File tree

FileSyncLibNet/SyncProviders/SmbLibProvider.cs

Lines changed: 224 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,12 @@ internal class SmbLibProvider : ProviderBase
1515
{
1616
SMB2Client client;
1717
ISMBFileStore fileStore;
18-
string Server
19-
{
20-
get
21-
{
22-
if (JobOptions.DestinationPath.StartsWith("smb://", StringComparison.OrdinalIgnoreCase)) //Linux syntax
23-
{
24-
//Pattern smb://ServerName/ShareName/Folder1/Folder2
25-
return JobOptions.DestinationPath.Substring(1);
26-
}
27-
else
28-
{
29-
//Pattern \\ServerName\ShareName\Folder1\Folder2
30-
return (JobOptions.DestinationPath.StartsWith("\\\\") ? JobOptions.DestinationPath.Substring(2) : JobOptions.DestinationPath).Split('\\')[0];
31-
}
32-
}
33-
}
34-
string Share
35-
{
36-
get
37-
{
38-
var remainingDestination = JobOptions.DestinationPath.Substring(JobOptions.DestinationPath.IndexOf(Server) + Server.Length);
39-
return remainingDestination.Trim('/', '\\').Split('/', '\\')[0];
40-
}
41-
}
42-
string DestinationPath
43-
{
44-
get
45-
{
46-
return JobOptions.DestinationPath.Substring(JobOptions.DestinationPath.IndexOf(Share) + Share.Length).Replace('/', '\\');
47-
}
48-
}
4918

50-
public SmbLibProvider(IFileJobOptions options):base(options)
19+
20+
21+
22+
23+
public SmbLibProvider(IFileJobOptions options) : base(options)
5124
{
5225

5326
}
@@ -59,65 +32,164 @@ public override void SyncSourceToDest()
5932
{
6033
if (!(JobOptions is IFileSyncJobOptions jobOptions))
6134
throw new ArgumentException("this instance has no information about syncing files, it has type " + JobOptions.GetType().ToString());
62-
Directory.CreateDirectory(jobOptions.SourcePath);
63-
//Dateien ins Backup kopieren
64-
if (JobOptions.Credentials != null)
65-
{
66-
ConnectToShare(Server, Share, JobOptions.Credentials.Domain, JobOptions.Credentials.UserName, JobOptions.Credentials.Password);
67-
}
6835

69-
DirectoryInfo _di = new DirectoryInfo(jobOptions.SourcePath);
36+
//Determine if source or destination is network path
37+
bool sourceIsNetShare = jobOptions.SourcePath.StartsWith("\\\\");
38+
bool destIsNetShare = jobOptions.DestinationPath.StartsWith("\\\\");
39+
if (sourceIsNetShare && destIsNetShare)
40+
throw new NotImplementedException("both source and destination are network shares - this is not supported yet");
41+
42+
if (!sourceIsNetShare && !destIsNetShare)
43+
throw new Exception("neither source or destination are network share paths - unable to handle with SmbLibProvider");
7044

71-
foreach (var dir in JobOptions.Subfolders.Count > 0 ? _di.GetDirectories() : new[] { _di })
45+
if (!sourceIsNetShare && destIsNetShare)
7246
{
73-
if (JobOptions.Subfolders.Count > 0 && !JobOptions.Subfolders.Select(x => x.ToLower()).Contains(dir.Name.ToLower()))
74-
continue;
47+
Directory.CreateDirectory(jobOptions.SourcePath);
7548

49+
string DestinationServer = JobOptions.DestinationPath.StartsWith("smb://", StringComparison.OrdinalIgnoreCase) ?
50+
JobOptions.DestinationPath.Substring(1) :
51+
(JobOptions.DestinationPath.StartsWith("\\\\") ? JobOptions.DestinationPath.Substring(2) : JobOptions.DestinationPath).Split('\\')[0];
7652

53+
string DestinationShare = JobOptions.DestinationPath.Substring(JobOptions.DestinationPath.IndexOf(DestinationServer) + DestinationServer.Length).Trim('/', '\\').Split('/', '\\')[0];
54+
string DestinationPath = JobOptions.DestinationPath.Substring(JobOptions.DestinationPath.IndexOf(DestinationShare) + DestinationShare.Length).Replace('/', '\\');
7755

78-
var _fi = dir.EnumerateFiles(
79-
searchPattern: JobOptions.SearchPattern,
80-
searchOption: JobOptions.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
81-
82-
if (jobOptions.SyncDeleted)
56+
57+
58+
59+
//Dateien ins Backup kopieren
60+
if (JobOptions.Credentials != null)
8361
{
84-
var remoteFiles = ListFiles(DestinationPath, true);
85-
foreach (var file in remoteFiles)
86-
{
87-
var realFilePath = file.Substring(file.IndexOf(DestinationPath) + DestinationPath.Length).Trim('\\').Replace('/', '\\');
88-
if (!_fi.Any(x => x.FullName.Replace('/', '\\').EndsWith(realFilePath)))
89-
DeleteFile(file);
90-
}
62+
ConnectToShare(DestinationServer, DestinationShare, JobOptions.Credentials.Domain, JobOptions.Credentials.UserName, JobOptions.Credentials.Password);
9163
}
92-
foreach (FileInfo f in _fi)
64+
else
9365
{
94-
bool copy = false;
95-
var relativeFilename = f.FullName.Substring(Path.GetFullPath(jobOptions.SourcePath).Length);
96-
var remotefile = Path.Combine(DestinationPath, relativeFilename.TrimStart('\\', '/')).Replace('/', '\\');
97-
var exists = FileExists(remotefile, out long size);
98-
copy = !exists || size != f.Length;
99-
if (copy)
66+
throw new ArgumentNullException("Credentials are not set - cannot connect to share");
67+
}
68+
69+
DirectoryInfo _di = new DirectoryInfo(jobOptions.SourcePath);
70+
71+
foreach (var dir in JobOptions.Subfolders.Count > 0 ? _di.GetDirectories() : new[] { _di })
72+
{
73+
if (JobOptions.Subfolders.Count > 0 && !JobOptions.Subfolders.Select(x => x.ToLower()).Contains(dir.Name.ToLower()))
74+
continue;
75+
76+
77+
var _fi = dir.EnumerateFiles(
78+
searchPattern: JobOptions.SearchPattern,
79+
searchOption: JobOptions.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
80+
81+
if (jobOptions.SyncDeleted)
10082
{
101-
logger.LogDebug("Copy {A}", relativeFilename);
102-
//File.Copy(f.FullName, remotefile.FullName, true);
103-
//CopyFileWithBuffer(f, remotefile);
104-
try
83+
var remoteFiles = ListFiles(DestinationPath, true);
84+
foreach (var file in remoteFiles)
10585
{
106-
WriteFile(f.FullName, remotefile);
107-
if (jobOptions.DeleteSourceAfterBackup)
86+
var realFilePath = file.Substring(file.IndexOf(DestinationPath) + DestinationPath.Length).Trim('\\').Replace('/', '\\');
87+
if (!_fi.Any(x => x.FullName.Replace('/', '\\').EndsWith(realFilePath)))
88+
DeleteFile(file);
89+
}
90+
}
91+
foreach (FileInfo f in _fi)
92+
{
93+
bool copy = false;
94+
var relativeFilename = f.FullName.Substring(Path.GetFullPath(jobOptions.SourcePath).Length);
95+
var remotefile = Path.Combine(DestinationPath, relativeFilename.TrimStart('\\', '/')).Replace('/', '\\');
96+
var exists = FileExists(remotefile, out long size);
97+
copy = !exists || size != f.Length;
98+
if (copy)
99+
{
100+
logger.LogDebug("Copy {A}", relativeFilename);
101+
try
102+
{
103+
WriteFile(f.FullName, remotefile);
104+
if (jobOptions.DeleteSourceAfterBackup)
105+
{
106+
File.Delete(f.FullName);
107+
}
108+
}
109+
catch (Exception exc)
108110
{
109-
File.Delete(f.FullName);
111+
logger.LogError(exc, "Exception in WriteFile for {A}", relativeFilename);
110112
}
113+
111114
}
112-
catch (Exception exc)
115+
else
116+
logger.LogDebug("Skip {A}", relativeFilename);
117+
}
118+
}
119+
}
120+
else //(sourceIsNetShare && !destIsNetShare)
121+
{
122+
string SourceServer = jobOptions.SourcePath.StartsWith("smb://", StringComparison.OrdinalIgnoreCase) ?
123+
jobOptions.SourcePath.Substring(1) :
124+
(jobOptions.SourcePath.StartsWith("\\\\") ? jobOptions.SourcePath.Substring(2) : jobOptions.SourcePath).Split('\\')[0];
125+
126+
string SourceShare = jobOptions.SourcePath.Substring(jobOptions.SourcePath.IndexOf(SourceServer) + SourceServer.Length).Trim('/', '\\').Split('/', '\\')[0];
127+
string SourcePath = jobOptions.SourcePath.Substring(jobOptions.SourcePath.IndexOf(SourceShare) + SourceShare.Length).Replace('/', '\\');
128+
129+
if (JobOptions.Credentials != null)
130+
{
131+
ConnectToShare(SourceServer, SourceShare, JobOptions.Credentials.Domain, JobOptions.Credentials.UserName, JobOptions.Credentials.Password);
132+
}
133+
else
134+
{
135+
throw new ArgumentNullException("Credentials are not set - cannot connect to share");
136+
}
137+
138+
DirectoryInfo _di = new DirectoryInfo(jobOptions.DestinationPath);
139+
140+
foreach (var dir in JobOptions.Subfolders.Count > 0 ? _di.GetDirectories() : new[] { _di })
141+
{
142+
if (JobOptions.Subfolders.Count > 0 && !JobOptions.Subfolders.Select(x => x.ToLower()).Contains(dir.Name.ToLower()))
143+
continue;
144+
145+
146+
var _fi = dir.EnumerateFiles(
147+
searchPattern: JobOptions.SearchPattern,
148+
searchOption: JobOptions.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
149+
150+
var remoteFiles = ListFiles(SourcePath, true);
151+
if (jobOptions.SyncDeleted)
152+
{
153+
foreach (var file in remoteFiles)
113154
{
114-
logger.LogError(exc, "Exception in WriteFile for {A}", relativeFilename);
155+
var realFilePath = file.Substring(file.IndexOf(SourcePath) + SourcePath.Length).Trim('\\').Replace('/', '\\');
156+
if (!_fi.Any(x => x.FullName.Replace('/', '\\').EndsWith(realFilePath)))
157+
File.Delete(file);
115158
}
159+
}
160+
foreach (var remoteFile in remoteFiles)
161+
{
162+
bool copy = false;
163+
var realFilePath = remoteFile.Substring(remoteFile.IndexOf(SourcePath) + SourcePath.Length).Trim('\\').Replace('/', '\\');
164+
165+
var localFile = Path.Combine(jobOptions.DestinationPath, realFilePath.TrimStart('\\', '/')).Replace('/', '\\');
166+
var exists = File.Exists(localFile);
167+
_ = FileExists(remoteFile, out long remoteSize);
168+
var size = exists ? new FileInfo(localFile).Length : 0;
169+
copy = !exists || size != remoteSize;
170+
if (copy)
171+
{
172+
logger.LogDebug("Copy {A}", realFilePath);
173+
try
174+
{
175+
ReadFile(remoteFile, localFile);
176+
if (jobOptions.DeleteSourceAfterBackup)
177+
{
178+
DeleteFile(remoteFile);
179+
}
180+
}
181+
catch (Exception exc)
182+
{
183+
logger.LogError(exc, "Exception in ReadFile for {A}", realFilePath);
184+
}
116185

186+
}
187+
else
188+
logger.LogDebug("Skip {A}", realFilePath);
117189
}
118-
else
119-
logger.LogDebug("Skip {A}", relativeFilename);
120190
}
191+
192+
121193
}
122194
}
123195

@@ -133,7 +205,7 @@ public void ConnectToShare(string server, string shareName, string domain, strin
133205
bool isConnected = client.Connect(server, SMBTransportType.DirectTCPTransport);
134206
if (isConnected)
135207
{
136-
logger.LogDebug("ConnectToShare with domain {A} and user {B} with {C} pass", domain, user, password.Select(x=>'*'));
208+
logger.LogDebug("ConnectToShare with domain {A} and user {B} with {C} pass", domain, user, password.Select(x => '*'));
137209
status = client.Login(domain, user, password);
138210
if (status == NTStatus.STATUS_SUCCESS)
139211
{
@@ -161,6 +233,45 @@ public void Dispose()
161233
fileStore?.Disconnect();
162234
}
163235

236+
public void ReadFile(string remoteFilePath, string localFilePath)
237+
{
238+
object fileHandle;
239+
FileStatus fileStatus;
240+
241+
var status = fileStore.CreateFile(out fileHandle, out fileStatus, remoteFilePath, AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, FileAttributes.Normal, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
242+
243+
if (status == NTStatus.STATUS_SUCCESS)
244+
{
245+
Directory.CreateDirectory(Path.GetDirectoryName(localFilePath));
246+
var stream = File.Open(localFilePath, FileMode.Create, FileAccess.Write, FileShare.Read);
247+
byte[] data;
248+
long bytesRead = 0;
249+
while (true)
250+
{
251+
status = fileStore.ReadFile(out data, fileHandle, bytesRead, (int)client.MaxReadSize);
252+
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
253+
{
254+
throw new Exception("Failed to read from file");
255+
}
256+
257+
if (status == NTStatus.STATUS_END_OF_FILE || data.Length == 0)
258+
{
259+
break;
260+
}
261+
bytesRead += data.Length;
262+
stream.Write(data, 0, data.Length);
263+
}
264+
stream.Close();
265+
}
266+
if (fileHandle != null)
267+
status = fileStore.CloseFile(fileHandle);
268+
var fileInfo = new FileInfo(localFilePath);
269+
GetFileAttributes(remoteFilePath, out DateTime lastWriteTime, out DateTime creationTime, out _, out DateTime lastAccessTime);
270+
fileInfo.LastWriteTime = lastWriteTime;
271+
fileInfo.CreationTime = creationTime;
272+
fileInfo.LastAccessTime = lastAccessTime;
273+
}
274+
164275
public void WriteFile(string localFilePath, string remoteFilePath)
165276
{
166277
NTStatus status;
@@ -283,6 +394,46 @@ bool FileExists(string filepathFromShare, out long size)
283394
return false;
284395

285396
}
397+
void SetFileAttributes(string filepathFromShare, DateTime lastWriteTime, DateTime createTime, DateTime modifiedTime, DateTime accessTime)
398+
{
399+
object directoryHandle;
400+
FileStatus fileStatus;
401+
var status = fileStore.CreateFile(out directoryHandle, out fileStatus, filepathFromShare.Trim('\\'), AccessMask.GENERIC_READ, FileAttributes.Normal, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
402+
if (status == NTStatus.STATUS_SUCCESS)
403+
{
404+
405+
status = fileStore.GetFileInformation(out FileInformation result, directoryHandle, FileInformationClass.FileBasicInformation);
406+
(result as FileBasicInformation).LastWriteTime=lastWriteTime;
407+
(result as FileBasicInformation).CreationTime=createTime;
408+
(result as FileBasicInformation).ChangeTime=modifiedTime;
409+
(result as FileBasicInformation).LastAccessTime=accessTime;
410+
status = fileStore.SetFileInformation(directoryHandle, result);
411+
status = fileStore.CloseFile(directoryHandle);
412+
}
413+
throw new Exception("unable to set attributes - status " + status);
414+
415+
}
416+
void GetFileAttributes(string filepathFromShare, out DateTime lastWriteTime, out DateTime createTime, out DateTime modifiedTime, out DateTime accessTime)
417+
{
418+
object directoryHandle;
419+
FileStatus fileStatus;
420+
var status = fileStore.CreateFile(out directoryHandle, out fileStatus, filepathFromShare.Trim('\\'), AccessMask.GENERIC_READ, FileAttributes.Normal, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE, null);
421+
if (status == NTStatus.STATUS_SUCCESS)
422+
{
423+
424+
status = fileStore.GetFileInformation(out FileInformation result, directoryHandle, FileInformationClass.FileBasicInformation);
425+
lastWriteTime = (result as FileBasicInformation).LastWriteTime.Time.Value;
426+
modifiedTime = (result as FileBasicInformation).ChangeTime.Time.Value;
427+
createTime = (result as FileBasicInformation).CreationTime.Time.Value;
428+
accessTime = (result as FileBasicInformation).LastAccessTime.Time.Value;
429+
status = fileStore.CloseFile(directoryHandle);
430+
}
431+
else
432+
{
433+
throw new Exception("unable to set attributes - status " + status);
434+
}
435+
436+
}
286437

287438

288439

0 commit comments

Comments
 (0)