Copy permissions from file to file - c#

I need to replace the content of a file, without altering it's permissions. I'm doing it by readiong the file, deleting it and writing a new one with updated content.
I have the following:
static void Main()
{
var file = new FileInfo(#"C:\temp\test.txt");
var file1Security = file.GetAccessControl(AccessControlSections.All);
string s;
using (var stream = file.OpenText())
{
s = stream.ReadToEnd();
}
s += "\n" + DateTime.Now;
file.Delete();
using (var stream = file.OpenWrite())
{
using (var writer = new StreamWriter(stream))
{
writer.Write(s);
}
}
file.SetAccessControl(file1Security);
}
However, this doesn't copy the users' permissions over to the new file.
How do I replace the content of a file and preserve the users' permissions on it?

According to this documentation, you can't copy a FileSecurity from one file and apply it to another. (Apparently that came up enough that they documented it. I would have tried it too.)
You have to create a new FileSecurity object, copy the access control list from the old one to the new one, and then apply the new one to the file.
void ApplySecurityFromOneFileToAnother(FileInfo source, FileInfo destination)
{
var sourceSecurityDescriptor = source.GetAccessControl().GetSecurityDescriptorBinaryForm();
var targetSecurity = new FileSecurity();
targetSecurity.SetSecurityDescriptorBinaryForm(sourceSecurityDescriptor);
destination.SetAccessControl(targetSecurity);
}
Since you're replacing a file you'd have to break that up, of course - first get the security from the old file, then apply it to the same file after it's rewritten.

FileSecurity file1Security = file.GetAccessControl(AccessControlSections.All);
file1Security.SetAccessRuleProtection(true, true);
Try replacing "var" with the first line of code copied here, and add the line underneath. This final part applies the access rules.

Try to use DirectoryInfo at the beginning:
DirectoryInfo dInfo = new DirectoryInfo(#"C:\temp\test.txt");
DirectorySecurity dSecurity = dInfo.GetAccessControl();
and change the last line with:
dInfo.SetAccessControl(dSecurity);

Related

XML file from ZIP Archive is incomplete in C#

I've work with large XML Files (~1000000 lines, 34mb) that are stored in a ZIP archive. The XML file is used at runtime to store and load app settings and measurements. The gets loadeted with this function:
public static void LoadFile(string path, string name)
{
using (var file = File.OpenRead(path))
{
using (var zip = new ZipArchive(file, ZipArchiveMode.Read))
{
var foundConfigurationFile = zip.Entries.First(x => x.FullName == ConfigurationFileName);
using (var stream = new StreamReader(foundConfigurationFile.Open()))
{
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
var newObject = xmlSerializer.Deserialize(stream);
CurrentConfiguration = null;
CurrentConfiguration = newObject as ProjectConfiguration;
AddRecentFiles(name, path);
}
}
}
}
This works for most of the time.
However, some files don't get read to the end and i get an error that the file contains non valid XML. I used
foundConfigurationFile.ExtractToFile();
and fount that the readed file stops at line ~800000. But this only happens inside this code. When i open the file via editor everything is there.
It looks like the zip doesnt get loaded correctly, or for that matter, completly.
Am i running in some limitations? Or is there an error in my code i don't find?
The file is saved via:
using (var file = File.OpenWrite(Path.Combine(dirInfo.ToString(), fileName.ToString()) + ".pwe"))
{
var zip = new ZipArchive(file, ZipArchiveMode.Create);
var configurationEntry = zip.CreateEntry(ConfigurationFileName, CompressionLevel.Optimal);
var stream = configurationEntry.Open();
var xmlSerializer = new XmlSerializer(typeof(ProjectConfiguration));
xmlSerializer.Serialize(stream, CurrentConfiguration);
stream.Close();
zip.Dispose();
}
Update:
The problem was the File.OpenWrite() method.
If you try to override a file with this method it will result in a mix between the old file and the new file, if the new file is shorter than the old file.
File.OpenWrite() doenst truncate the old file first as stated in the docs
In order to do it correctly it was neccesary to use the File.Create() method. Because this method truncates the old file first.

How to open PowerPoint file in resource file C#

I have a number of small PowerPoint files in my resources folder and I want to open them. I'm having issues doing this as my Resource.sendToPPTTemp is of type byte[] and to open the file I need it as a string. Is there a way I can open a file from resources as a string?
var file = Resources.sendToPPTTemp;
ppnt.Application ppntApplication = new ppnt.Application();
var _assembly = Assembly.GetExecutingAssembly();
var myppnt = ppntApplication.Presentations.Open(file.ToString());
ppntApplication.Visible = MsoTriState.msoTrue;
You need to give the path to your file to the Open method, not the binary representation. Either you have the path and pass it to the method or you have to create a file with your byte[].
I'd rather create a folder with all your PPT and store in your resource file the path to that folder. Then you can use the first method:
var di = new DirectoryInfo(Resources.PPTFolderPath);
foreach(var file in di.GetFiles())
{
var myppnt = ppntApplication.Presentations.Open(fi.FullName);
ppntApplication.Visible = MsoTriState.msoTrue;
[..]
}
But if you really want to store your PPT in the resource file, you can do it like this, with a temporary file for example:
var tmpPath = Path.GetTempFileName();
try
{
File.WriteAllBytes(tmpPath, Resources.sendToPPTTemp);
var myppnt = ppntApplication.Presentations.Open(tmpPath);
ppntApplication.Visible = MsoTriState.msoTrue;
[..]
}
finally
{
// you have to delete your tmp file at the end!!!
// probably not the better way to do it because I guess the program does not block on Open.
// Better store the file path into a list and delete later.
var fi = new FileInfo(tmpPath);
fi.Delete();
}

Enumerate zipped contents of unzipped folder

I am trying to enumerate the zipped folders that are inside an unzipped folder using Directory.GetDirectories(folderPath).
The problem I have is that it does not seem to be finding the zipped folders, when I come to iterate over the string[], it is empty.
Is Directory.GetDirectories() the wrong way to go about this and if so what method serves this purpose?
Filepath example: C:\...\...\daily\daily\{series of zipped folder}
public void CheckZippedDailyFolder(string folderPath)
{
if(folderPath.IsNullOrEmpty())
throw new Exception("Folder path required");
foreach (var folder in Directory.GetDirectories(folderPath))
{
var unzippedFolder = Compression.Unzip(folder + ".zip", folderPath);
using (TextReader reader = File.OpenText(unzippedFolder + #"\" + new DirectoryInfo(folderPath).Name))
{
var csv = new CsvReader(reader);
var field = csv.GetField(0);
Console.WriteLine(field);
}
}
}
GetDirectories is the wrong thing to use. Explorer lies to you; zip files are actually files with an extension .zip, not real directories on the file system level.
Look at:
https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive.entries%28v=vs.110%29.aspx (ZipArchive.Entries) and/or
https://msdn.microsoft.com/en-us/library/system.io.compression.zipfile%28v=vs.110%29.aspx (ZipFile) to see how to deal with them.

How to use SharpSVN to modify file xml and commit modified file

am a SharpSVN newbie.
We are currently busy with rebranding, and this entails updating all our reports with new colours etc. There are too many reports to do manually, so I am trying to find a way to find and replace the colours/fonts etc in one go.
Our reports are serialized and stored in a database, which is easy to replace, but we also want to apply the changes in the .rdl reports in our source control, which is Subversion.
My question is the following:
I know you can write files to a stream with SharpSVN, which I have done, now I would like to push the updated xml back into Subversion as the latest version.
Is this at all possible? And if so, how would I go about doing this? I have googled alot, but haven't been able to find any definitive answer to this.
My code so far (keep in mind this is a once off thing, so I'm not too concerned about clean code etc) :
private void ReplaceFiles()
{
SvnCommitArgs args = new SvnCommitArgs();
SvnCommitResult result;
args.LogMessage = "Rebranding - Replace fonts, colours, and font sizes";
using (SvnClient client = new SvnClient())
{
client.Authentication.DefaultCredentials = new NetworkCredential("mkassa", "Welcome1");
client.CheckOut(SvnUriTarget.FromString(txtSubversionDirectory.Text), txtCheckoutDirectory.Text);
client.Update(txtCheckoutDirectory.Text);
SvnUpdateResult upResult;
client.Update(txtCheckoutDirectory.Text, out upResult);
ProcessDirectory(txtCheckoutDirectory.Text, args, client);
}
MessageBox.Show("Done");
}
// Process all files in the directory passed in, recurse on any directories
// that are found, and process the files they contain.
public void ProcessDirectory(string targetDirectory, SvnCommitArgs args, SvnClient client)
{
var ext = new List<string> { ".rdl" };
// Process the list of files found in the directory.
IEnumerable<string> fileEntries = Directory.EnumerateFiles(targetDirectory, "*.*", SearchOption.AllDirectories)
.Where(s => ext.Any(e=> s.EndsWith(e)));
foreach (string fileName in fileEntries)
ProcessFile(fileName, args, client);
}
private void ProcessFile(string fileName, SvnCommitArgs args, SvnClient client)
{
using (MemoryStream stream = new MemoryStream())
{
SvnCommitResult result;
if (client.Write(SvnTarget.FromString(fileName), stream))
{
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
string contents = reader.ReadToEnd();
DoReplacement(contents);
client.Commit(txtCheckoutDirectory.Text, args, out result);
//if (result != null)
// MessageBox.Show(result.PostCommitError);
}
}
}
}
Thank you to anyone who can provide some insight on this!
You don't want to perform a merge on the file, as you would only use that to merge the changes from one location into another location.
If you can't just checkout your entire tree and replace+commit on that, you might be able to use something based on:
string tmpDir = "C:\tmp\mytmp";
using(SvnClient svn = new SvnClient())
{
List<Uri> toProcess = new List<Uri>();
svn.List(new Uri("http://my-repos/trunk"), new SvnListArgs() { Depth=Infinity }),
delegate(object sender, SvnListEventArgs e)
{
if (e.Path.EndsWith(".rdl", StringComparison.OrdinalIgnoreCase))
toProcess.Add(e.Uri);
});
foreach(Uri i in toProcess)
{
Console.WriteLine("Processing {0}", i);
Directory.Delete(tmpDir, true);
// Create a sparse checkout with just one file (see svnbook.org)
string name = SvnTools.GetFileName(i);
string fileName = Path.Join(tmpDir, name)
svn.CheckOut(new Uri(toProcess, "./"), new SvnCheckOutArgs { Depth=Empty });
svn.Update(fileName);
ProcessFile(fileName); // Read file and save in same location
// Note that the following commit is a no-op if the file wasn't
// changed, so you don't have to check for that yourself
svn.Commit(fileName, new SvnCommitArgs { LogMessage="Processed" });
}
}
Once you updated trunk I would recommend merging that change to your maintenance branches... and if necessary only fix them after that. Otherwise further merges will be harder to perform than necessary.
I managed to get this done. Posting the answer for future reference.
Basically all I had to do was create a new .rdl file with the modified XML, and replace the checked out file with the new one before committing.
string contents = reader.ReadToEnd();
contents = DoReplacement(contents);
// Create an XML document
XmlDocument doc = new XmlDocument();
string xmlData = contents;
doc.Load(new StringReader(xmlData));
//Save XML document to file
doc.Save(fileName);
client.Commit(txtCheckoutDirectory.Text, args, out result);
Hopefully this will help anyone needing to do the same.

C#: Creating a new FileInfo in a directory that you have the DirectoryInfo of

I was just wondering when you have for example:
var dir = new DirectoryInfo(#"C:\Temp");
Is there an easier/clearer way to add a new file to that directory than this?
var file = new FileInfo(Path.Combine(dir.FullName, "file.ext"));
I'm thinking I can probably just make an extension method or something, but curious if something already exists that can't see here... I mean the DirectoryInfo does have GetFiles() method for example.
What is it that you want to do? The title says "Creating a new file". A FileInfo object is not a file; it's an object holding information about a file (that may or may not exist). If you actually want to create the file, there are a number of ways of doing so. One of the simplest ways would be this:
File.WriteAllText(Path.Combine(dir.FullName, "file.ext"), "some text");
If you want to create the file based on the FileInfo object instead, you can use the following approach:
var dir = new DirectoryInfo(#"C:\Temp");
var file = new FileInfo(Path.Combine(dir.FullName, "file.ext"));
if (!file.Exists) // you may not want to overwrite existing files
{
using (Stream stream = file.OpenWrite())
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write("some text");
}
}
As a side note: it is dir.FullName, not dir.FullPath.
Why don't you use:
File.Create(#"C:\Temp\file.ext");
or
var dir = new DirectoryInfo(#"C:\Temp");
File.Create(dir.FullName + "\\file.ext");
While there does exist Directorynfo.GetFiles() methods, they only return files that actually exist on disk. Path.Combine is about hypothetical paths.
Try these extension methods:
public static FileInfo CombineWithFileName(this DirectoryInfo directoryInfo, string fileName)
{
return new FileInfo(Path.Combine(directoryInfo.Name, fileName));
}
public static DirectoryInfo CombineWithDirectoryName(this DirectoryInfo directoryInfo, string directoryName)
{
return new DirectoryInfo(Path.Combine(directoryInfo.Name, directoryName));
}

Categories