throw new NotImplementedException();
}
+ public void ClearFolderStatus(string path)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable<FileState> GetChildren(FileState fileState)
+ {
+ throw new NotImplementedException();
+ }
+
private PithosStatus _pithosStatus = PithosStatus.InSynch;
public void SetPithosStatus(PithosStatus status)
public class CloudDeleteAction:CloudAction
{
public CloudDeleteAction(AccountInfo accountInfo,FileSystemInfo fileInfo, FileState fileState)
- : this(accountInfo,fileInfo,new ObjectInfo(accountInfo.AccountPath,accountInfo.UserName,fileInfo),fileState)
+ : this(accountInfo,fileInfo,new ObjectInfo(accountInfo.AccountPath,accountInfo.UserName,fileInfo.WithProperCapitalization()),fileState)
{
}
var filePath = e.FullPath;
if (Ignore(filePath))
return;
- /* if (Directory.Exists(filePath))
- return; */
+
_agent.Post(new WorkflowState{AccountInfo=AccountInfo, Path = filePath, FileName = e.Name, TriggeringChange = e.ChangeType });
}
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using System.Threading.Tasks.Dataflow;
using Castle.ActiveRecord;
using Pithos.Interfaces;
using Pithos.Network;
private Agent<CloudAction> _agent;
//A separate agent is used to execute delete actions immediatelly;
- private Agent<CloudDeleteAction> _deleteAgent;
+ private ActionBlock<CloudDeleteAction> _deleteAgent;
readonly ConcurrentDictionary<string,DateTime> _deletedFiles=new ConcurrentDictionary<string, DateTime>();
[System.ComponentModel.Composition.Import]
loop();
});
- _deleteAgent = Agent<CloudDeleteAction>.Start(inbox =>
- {
+ _deleteAgent = new ActionBlock<CloudDeleteAction>(message =>ProcessDelete(message),new ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=4});
+ /*
Action loop = null;
loop = () =>
{
inbox.LoopAsync(process, loop);
};
loop();
+*/
- });
}
private async Task Process(CloudAction action)
if (IsDeletedFile(action))
{
//Clear the status of already deleted files to avoid reprocessing
- this.StatusKeeper.ClearFileStatus(action.LocalFile.FullName);
+ if (action.LocalFile != null)
+ this.StatusKeeper.ClearFileStatus(action.LocalFile.FullName);
}
else
{
var key = GetFileKey(action.CloudFile);
_deletedFiles[key]=DateTime.Now;
+
// and then delete the file from the server
DeleteCloudFile(accountInfo, cloudFile);
+
Log.InfoFormat("[ACTION] End Delete {0}:{1}->{2}", action.Action, action.LocalFile,
action.CloudFile.Name);
}
//If the directory doesn't exist the Hash property will be empty
if (String.IsNullOrWhiteSpace(info.Hash))
//Go on and create the directory
- client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName, String.Empty, "application/directory");
+ await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName, String.Empty, "application/directory");
}
else
{
if (fileState == null)
{
//This is a new file
- var fullPath = pair.File.FullName.ToLower();
+ var fullPath = pair.File.FullName;
var createState = FileState.CreateForAsync(fullPath, BlockSize, BlockHash);
createState.ContinueWith(state => _persistenceAgent.Post(state.Result.Create));
}
using (var connection = GetConnection())
using (
var command =
- new SQLiteCommand("update FileState set FileStatus= :fileStatus where FilePath = :path ",
+ new SQLiteCommand("update FileState set FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE",
connection))
{
command.Parameters.AddWithValue("fileStatus", status);
- command.Parameters.AddWithValue("path", path.ToLower());
+ command.Parameters.AddWithValue("path", path);
var affected = command.ExecuteNonQuery();
return affected;
using (
var command =
new SQLiteCommand(
- "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path ",
+ "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE ",
connection))
{
- command.Parameters.AddWithValue("path", absolutePath.ToLower());
+ command.Parameters.AddWithValue("path", absolutePath);
command.Parameters.AddWithValue("fileStatus", fileStatus);
command.Parameters.AddWithValue("overlayStatus", overlayStatus);
{
using (var connection = GetConnection())
- using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum ,Version ,VersionTimeStamp,IsShared ,SharedBy ,ShareWrite from FileState where FilePath=:path", connection))
+ using (var command = new SQLiteCommand("select Id, FilePath, OverlayStatus,FileStatus ,Checksum ,Version ,VersionTimeStamp,IsShared ,SharedBy ,ShareWrite from FileState where FilePath=:path COLLATE NOCASE", connection))
{
- command.Parameters.AddWithValue("path", path.ToLower());
+ command.Parameters.AddWithValue("path", path);
using (var reader = command.ExecuteReader())
{
{
using (var connection = GetConnection())
- using (var command = new SQLiteCommand("select OverlayStatus from FileState where FilePath=:path", connection))
+ using (var command = new SQLiteCommand("select OverlayStatus from FileState where FilePath=:path COLLATE NOCASE", connection))
{
- command.Parameters.AddWithValue("path", path.ToLower());
+ command.Parameters.AddWithValue("path", path);
var s = command.ExecuteScalar();
return (FileOverlayStatus) Convert.ToInt32(s);
throw new ArgumentException("The path must be rooted","path");
Contract.EndContractBlock();
- _persistenceAgent.Post(() => FileState.StoreOverlayStatus(path.ToLower(),overlayStatus));
+ _persistenceAgent.Post(() => FileState.StoreOverlayStatus(path,overlayStatus));
}
/* public void RenameFileOverlayStatus(string oldPath, string newPath)
Debug.Assert(!path.Contains(FolderConstants.CacheFolder));
Debug.Assert(!path.EndsWith(".ignore"));
- _persistenceAgent.Post(() => UpdateStatusDirect(path.ToLower(), fileStatus, overlayStatus));
+ _persistenceAgent.Post(() => UpdateStatusDirect(path, fileStatus, overlayStatus));
}
/*
{
if (StateExists(path, connection))
command.CommandText =
- "update FileState set FileStatus= :fileStatus where FilePath = :path ";
+ "update FileState set FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE ";
else
{
command.CommandText =
command.Parameters.AddWithValue("id", Guid.NewGuid());
}
- command.Parameters.AddWithValue("path", path.ToLower());
+ command.Parameters.AddWithValue("path", path);
command.Parameters.AddWithValue("checksum", objectInfo.Hash);
command.Parameters.AddWithValue("version", objectInfo.Version);
command.Parameters.AddWithValue("versionTimeStamp",
private bool StateExists(string filePath,SQLiteConnection connection)
{
- using (var command = new SQLiteCommand("Select count(*) from FileState where FilePath=:path", connection))
+ using (var command = new SQLiteCommand("Select count(*) from FileState where FilePath=:path COLLATE NOCASE", connection))
{
- command.Parameters.AddWithValue("path", filePath.ToLower());
+ command.Parameters.AddWithValue("path", filePath);
var result = command.ExecuteScalar();
return ((long)result >= 1);
}
throw new ArgumentException("The path must be rooted", "path");
Contract.EndContractBlock();
- _persistenceAgent.Post(() => UpdateStatusDirect(path.ToLower(), status));
+ _persistenceAgent.Post(() => UpdateStatusDirect(path, status));
}
public FileStatus GetFileStatus(string path)
using (var connection = GetConnection())
{
- var command = new SQLiteCommand("select FileStatus from FileState where FilePath=:path", connection);
- command.Parameters.AddWithValue("path", path.ToLower());
+ var command = new SQLiteCommand("select FileStatus from FileState where FilePath=:path COLLATE NOCASE", connection);
+ command.Parameters.AddWithValue("path", path);
var s = command.ExecuteScalar();
return (FileStatus)Convert.ToInt32(s);
}
}
+ /// <summary>
+ /// Deletes the status of the specified file
+ /// </summary>
+ /// <param name="path"></param>
public void ClearFileStatus(string path)
{
if (String.IsNullOrWhiteSpace(path))
_persistenceAgent.Post(() => DeleteDirect(path));
}
+ /// <summary>
+ /// Deletes the status of the specified folder and all its contents
+ /// </summary>
+ /// <param name="path"></param>
+ public void ClearFolderStatus(string path)
+ {
+ if (String.IsNullOrWhiteSpace(path))
+ throw new ArgumentNullException("path");
+ if (!Path.IsPathRooted(path))
+ throw new ArgumentException("The path must be rooted", "path");
+ Contract.EndContractBlock();
+
+ _persistenceAgent.Post(() => DeleteFolderDirect(path));
+ }
+
+ public IEnumerable<FileState> GetChildren(FileState fileState)
+ {
+ if (fileState == null)
+ throw new ArgumentNullException("fileState");
+ Contract.EndContractBlock();
+
+ var children = from state in FileState.Queryable
+ where state.FilePath.StartsWith(fileState.FilePath + "\\")
+ select state;
+ return children;
+ }
+
private int DeleteDirect(string filePath)
{
using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
using (var connection = GetConnection())
{
- var command = new SQLiteCommand("delete from FileState where FilePath = :path",
+ var command = new SQLiteCommand("delete from FileState where FilePath = :path COLLATE NOCASE",
+ connection);
+
+ command.Parameters.AddWithValue("path", filePath);
+
+ var affected = command.ExecuteNonQuery();
+ return affected;
+ }
+ }
+ catch (Exception exc)
+ {
+ Log.Error(exc.ToString());
+ throw;
+ }
+ }
+ }
+
+ private int DeleteFolderDirect(string filePath)
+ {
+ using (log4net.ThreadContext.Stacks["StatusAgent"].Push("DeleteDirect"))
+ {
+
+ try
+ {
+
+
+ using (var connection = GetConnection())
+ {
+ var command = new SQLiteCommand("delete from FileState where FilePath = :path or FilePath like :path + '/%' COLLATE NOCASE",
connection);
- command.Parameters.AddWithValue("path", filePath.ToLower());
+ command.Parameters.AddWithValue("path", filePath);
var affected = command.ExecuteNonQuery();
return affected;
throw new ArgumentException("The path must be rooted", "path");
Contract.EndContractBlock();
- _persistenceAgent.Post(() => FileState.UpdateChecksum(path.ToLower(), checksum));
+ _persistenceAgent.Post(() => FileState.UpdateChecksum(path, checksum));
}
}
if (Log.IsDebugEnabled) Log.DebugFormat("Skipping {0}", state.FileName);
return CompletedTask<object>.Default;
- }
- string path = state.Path.ToLower();
-
-
+ }
- FileSystemInfo info = Directory.Exists(path) ? (FileSystemInfo) new DirectoryInfo(path) : new FileInfo(path);
+ var info = Directory.Exists(state.Path) ? (FileSystemInfo)new DirectoryInfo(state.Path) : new FileInfo(state.Path);
//Bypass deleted files, unless the status is Deleted
if (!info.Exists && state.Status != FileStatus.Deleted)
{
state.Skip = true;
- this.StatusKeeper.ClearFileStatus(path);
+ this.StatusKeeper.ClearFileStatus(state.Path);
if (Log.IsDebugEnabled) Log.DebugFormat("Skipped missing {0}", state.FileName);
using (new SessionScope(FlushAction.Never))
{
- var fileState = StatusKeeper.GetStateByFilePath(path);
+ var fileState = StatusKeeper.GetStateByFilePath(state.Path);
switch (state.Status)
{
accountInfo.BlockHash));
break;
case FileStatus.Deleted:
+ if (fileState != null)
+ {
+ var children = StatusKeeper.GetChildren(fileState);
+ foreach (var child in children)
+ {
+ var childInfo = child.IsFolder
+ ? (FileSystemInfo) new DirectoryInfo(child.FilePath)
+ : new FileInfo(child.FilePath);
+ NetworkAgent.Post(new CloudDeleteAction(accountInfo, childInfo, child));
+ }
+ }
NetworkAgent.Post(new CloudDeleteAction(accountInfo, info, fileState));
break;
case FileStatus.Renamed:
{
private static readonly ILog Log = LogManager.GetLogger("FileState");
- private string _filePath;
private IList<FileTag> _tags = new List<FileTag>();
[PrimaryKey(PrimaryKeyType.Guid)]
public Guid Id { get; set; }
+
[Property(Unique = true, UniqueKey = "IX_FileState_FilePath")]
- public string FilePath
- {
- get { return _filePath; }
- set { _filePath = value.ToLower(); }
- }
+ public string FilePath { get; set; }
[Property]
public FileOverlayStatus OverlayStatus { get; set; }
[Property]
public bool ShareWrite { get; set; }
+ [Property]
+ public bool IsFolder{ get; set; }
[HasMany(Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Lazy = true, Inverse = true)]
public IList<FileTag> Tags
- return Queryable.FirstOrDefault(s => s.FilePath == absolutePath.ToLower());
+ return Queryable.FirstOrDefault(s => s.FilePath == absolutePath);
}
catch (Exception ex)
{
{
const string hqlDelete = "delete FileState where FilePath = :path";
var deletedEntities = session.CreateQuery(hqlDelete)
- .SetString("path", absolutePath.ToLower())
+ .SetString("path", absolutePath)
.ExecuteUpdate();
return deletedEntities;
}, null);
ExecuteWithRetry((session, instance) =>
{
- const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
+ const string hqlUpdate = "update FileState set FileStatus= :status where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("path", absolutePath.ToLower())
+ .SetString("path", absolutePath)
.SetEnum("status", newStatus)
.ExecuteUpdate();
if (updatedEntities == 0)
{
var newState = new FileState
{
- FilePath = absolutePath.ToLower(),
+ FilePath = absolutePath,
Id = Guid.NewGuid(),
- FileStatus = newStatus
+ FileStatus = newStatus,
+ IsFolder=Directory.Exists(absolutePath)
};
newState.CreateAndFlush();
}
ExecuteWithRetry((session, instance) =>
{
const string hqlUpdate =
- "update FileState set OverlayStatus= :status where FilePath = :path ";
- var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("path", absolutePath.ToLower())
- .SetEnum("status", newStatus)
+ "update FileState set OverlayStatus= :status where FilePath = :path ";
+ var updatedEntities = session.CreateQuery(hqlUpdate)
+ .SetString("path", absolutePath)
+ .SetEnum("status", newStatus)
.ExecuteUpdate();
if (updatedEntities == 0)
{
{
FilePath = absolutePath,
Id = Guid.NewGuid(),
- OverlayStatus = newStatus
+ OverlayStatus = newStatus,
+ IsFolder=Directory.Exists(absolutePath)
};
newState.CreateAndFlush();
}
const string hqlUpdate =
"update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("path", absolutePath.ToLower())
+ .SetString("path", absolutePath)
.SetEnum("fileStatus", fileStatus)
.SetEnum("overlayStatus", overlayStatus)
.ExecuteUpdate();
const string hqlUpdate =
"update FileState set FileStatus= :fileStatus where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("path", absolutePath.ToLower())
+ .SetString("path", absolutePath)
.SetEnum("fileStatus", fileStatus)
.ExecuteUpdate();
return updatedEntities;
ExecuteWithRetry((session, instance) =>
{
const string hqlUpdate =
- "update FileState set FilePath= :newPath where FilePath = :oldPath ";
+ "update FileState set FilePath= :newPath where FilePath = :oldPath ";
var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("oldPath", oldPath.ToLower())
- .SetString("newPath", newPath.ToLower())
+ .SetString("oldPath", oldPath)
+ .SetString("newPath", newPath)
.ExecuteUpdate();
return updatedEntities;
}, null);
ExecuteWithRetry((session, instance) =>
{
- const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
+ const string hqlUpdate = "update FileState set Checksum= :checksum where FilePath = :path ";
var updatedEntities = session.CreateQuery(hqlUpdate)
- .SetString("path", absolutePath.ToLower())
+ .SetString("path", absolutePath)
.SetString("checksum", checksum)
.ExecuteUpdate();
return updatedEntities;
const string hqlUpdate =
"update FileState set FilePath = replace(FilePath,:oldPath,:newPath) where FilePath like :oldPath || '%' ";
var renames = session.CreateQuery(hqlUpdate)
- .SetString("oldPath", oldPath.ToLower())
- .SetString("newPath", newPath.ToLower())
+ .SetString("oldPath", oldPath)
+ .SetString("newPath", newPath)
.ExecuteUpdate();
return renames;
}, null);
var fileState = new FileState
{
- FilePath = filePath.ToLower(),
+ FilePath = filePath,
OverlayStatus = FileOverlayStatus.Unversioned,
FileStatus = FileStatus.Created,
Id = Guid.NewGuid()
int BlockSize { get; set; }
void ChangeRoots(string oldPath, string newPath);
FileState GetStateByFilePath(string path);
+ void ClearFolderStatus(string path);
+ IEnumerable<FileState> GetChildren(FileState fileState);
}
[ContractClassFor(typeof(IStatusKeeper))]
return null;
}
+
+ public void ClearFolderStatus(string path)
+ {
+ Contract.Requires(!String.IsNullOrWhiteSpace(path));
+ Contract.Requires(Path.IsPathRooted(path));
+ }
+
+ public IEnumerable<FileState> GetChildren(FileState fileState)
+ {
+ Contract.Requires(fileState!=null);
+ Contract.Ensures(Contract.Result<IEnumerable<FileState>>()!=null);
+ return default(IEnumerable<FileState>);
+ }
}
}
throw new ArgumentNullException("fileInfo");
Contract.EndContractBlock();
- var relativeUrl = fileInfo.AsRelativeUrlTo(accountPath);
+ var relativeUrl = fileInfo.WithProperCapitalization().AsRelativeUrlTo(accountPath);
//The first part of the URL is the container
var slashIndex = relativeUrl.IndexOf('/');
var container = relativeUrl.Substring(0, slashIndex);
//The response stream must be copied to avoid affecting other code by disposing of the
//original response stream.
var stream = webResponse.GetResponseStream();
- using(var memStream=new MemoryStream((int) stream.Length))
+ using(var memStream=new MemoryStream())
using (var reader = new StreamReader(memStream))
{
stream.CopyTo(memStream);