Uploader.cs modified to check if it is possible to upload a new shared file before actually uploading
CloudFilesClient.CanUpload added that checks whether a file can be uploaded in the parent folder of a target object
Closes #2352
{
var createdState = FileState.CreateFor(FileInfoExtensions.FromPath(path));
createdState.FileStatus = status;
- _persistenceAgent.Post(createdState.Create);
+ createdState.Create();
}
return affected;
}
var createdState=FileState.CreateFor(FileInfoExtensions.FromPath(absolutePath));
createdState.FileStatus = fileStatus;
createdState.OverlayStatus = overlayStatus;
- _persistenceAgent.Post(createdState.Create);
+ createdState.Create();
}
return affected;
}
{
var client = new CloudFilesClient(accountInfo);
+
//Even if GetObjectInfo times out, we can proceed with the upload
var cloudInfo = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
- //If this is a read-only file, do not upload changes
- if (!cloudInfo.IsWritable(action.AccountInfo.UserName))
- return;
+ //If this a shared file
+ if (!cloudFile.Account.Equals(action.AccountInfo.UserName,StringComparison.InvariantCultureIgnoreCase))
+ {
+ //If this is a read-only file, do not upload changes
+ if (!cloudInfo.IsWritable(action.AccountInfo.UserName))
+ {
+ MakeFileReadOnly(fullFileName);
+ return;
+ }
- //TODO: If the object does not exist, check that we can upload to the folder
+ //If the file is new, can we upload it?
+ if ( !cloudInfo.Exists && !client.CanUpload(account, cloudFile))
+ {
+ MakeFileReadOnly(fullFileName);
+ return;
+ }
+
+ }
if (fileInfo is DirectoryInfo)
{
if (response.StatusCode == HttpStatusCode.Forbidden)
{
StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict, "Forbidden");
-
+ MakeFileReadOnly(fullFileName);
}
else
//In any other case, propagate the error
}
}
+ private static void MakeFileReadOnly(string fullFileName)
+ {
+ var attributes = File.GetAttributes(fullFileName);
+ //Do not make any modifications if not necessary
+ if (attributes.HasFlag(FileAttributes.ReadOnly))
+ return;
+ File.SetAttributes(fullFileName, attributes | FileAttributes.ReadOnly);
+ }
+
private static AccountInfo GetSharerAccount(string relativePath, AccountInfo accountInfo)
{
var parts = relativePath.Split('\\');
var info1 = new ObjectInfo {Account = "pkanavos", Container = "pithos", Name = "somefolder/file1.txt"};
var path1=info1.RelativeUrlToFilePath("PKANAVOS");
- Assert.AreEqual(@"somefolder\file1.txt",path1);
+ Assert.AreEqual(@"pithos\somefolder\file1.txt",path1);
var path2 = info1.RelativeUrlToFilePath("user1");
- Assert.AreEqual(@"others\pkanavos\somefolder\file1.txt", path2);
+ Assert.AreEqual(@"others-shared\pkanavos\pithos\somefolder\file1.txt", path2);
var info3 = new ObjectInfo { Account = "pkanavos", Name = "somefolder/file1.txt" };
var path3 = info1.RelativeUrlToFilePath("PKANAVOS");
- Assert.AreEqual(@"somefolder\file1.txt", path1);
+ Assert.AreEqual(@"pithos\somefolder\file1.txt", path1);
}
var permissionString = objectInfo.GetPermissionString();
Assert.IsNotNullOrEmpty(permissionString);
- Assert.AreEqual("read=user1;write=user2",permissionString);
+ Assert.AreEqual("read=user1;write=user2",Uri.UnescapeDataString(permissionString));
}
[Test]
Assert.IsTrue(objectInfo.Permissions["user3@somehost.gr"]=="write");
}
+
+ [Test]
+ public void empty_should_be_writable()
+ {
+ Assert.That(ObjectInfo.Empty.IsWritable("somAccount"),Is.True);
+ }
+
+ [Test]
+ public void should_be_writeable_for_write_permission()
+ {
+ var objectInfo = new ObjectInfo();
+ var permissionString = "read=user1;write=user2,user3@somehost.gr";
+ objectInfo.SetPermissions(permissionString);
+
+ Assert.That(objectInfo.IsWritable("user3@somehost.gr"),Is.True);
+
+ }
+
+ [Test]
+ public void should_not_be_writeable_for_read_permission()
+ {
+ var objectInfo = new ObjectInfo();
+ var permissionString = "read=user1;write=user2,user3@somehost.gr";
+ objectInfo.SetPermissions(permissionString);
+
+ Assert.That(objectInfo.IsWritable("user1"),Is.False);
+
+ }
+
+ [Test]
+ public void should_not_be_writeable_for_allowed()
+ {
+ var objectInfo = new ObjectInfo{AllowedTo="write"};
+ var permissionString = "read=user1;write=user2,user3@somehost.gr";
+ objectInfo.SetPermissions(permissionString);
+
+ Assert.That(objectInfo.IsWritable("user4"),Is.True);
+
+ }
}
}
var directories=this.ListObjects(container.Account, container.Name, "/");
}
*/
+
+ public bool CanUpload(string account, ObjectInfo cloudFile)
+ {
+ Contract.Requires(!String.IsNullOrWhiteSpace(account));
+ Contract.Requires(cloudFile!=null);
+
+ using (var client = new RestClient(_baseClient))
+ {
+ if (!String.IsNullOrWhiteSpace(account))
+ client.BaseAddress = GetAccountUrl(account);
+
+
+ var parts = cloudFile.Name.Split('/');
+ var folder = String.Join("/", parts,0,parts.Length-1);
+
+ var fileUrl=String.Format("{0}/{1}/{2}.pithos.ignore",cloudFile.Container,folder,Guid.NewGuid());
+
+ client.Parameters.Clear();
+ try
+ {
+ client.PutWithRetry(fileUrl, 3, @"application/octet-stream");
+
+ var expectedCodes = new[] { HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created};
+ return (expectedCodes.Contains(client.StatusCode));
+ }
+ catch
+ {
+ return false;
+ }
+ finally
+ {
+ DeleteObject(account,cloudFile.Container,fileUrl);
+ }
+ }
+ }
}
public class ShareAccountInfo
RetryWithoutContent(address, retries, "HEAD");
}
- public void PutWithRetry(string address, int retries = 0)
+ public void PutWithRetry(string address, int retries = 0, string contentType=null)
{
- RetryWithoutContent(address, retries, "PUT");
+ RetryWithoutContent(address, retries, "PUT",contentType);
}
public void PostWithRetry(string address,string contentType)