/// <summary> /// Initializes a new instance that provides access to the contents /// of a given directory. /// </summary> /// <param name="rootDirectory">The root folder which is being managed /// by this provider instance.</param> /// <param name="rootName">The <see cref="VirtualResourceInfo.Name"/> of /// the root item. Used to mask the real folder name.</param> /// <param name="useRelativePaths">If true, returned <see cref="VirtualResourceInfo"/> /// instances do not provide qualified paths, but virtual paths to the submitted /// root directory. This also leverages security in remote access scenarios.</param> /// <exception cref="ArgumentNullException">If <paramref name="rootDirectory"/> or /// <paramref name="rootName"/> is a null reference.</exception> /// <exception cref="DirectoryNotFoundException">If the directory does /// not exist on the local file system.</exception> public LocalFileSystemProviderX(DirectoryInfo rootDirectory, string rootName, bool useRelativePaths) { if (rootName == null) { throw new ArgumentNullException("rootName"); } if (rootDirectory == null) { throw new ArgumentNullException("rootDirectory"); } if (!rootDirectory.Exists) { string msg = "Root directory '{0}' does not exist."; msg = String.Format(msg, rootDirectory.FullName); VfsLog.Debug(msg); throw new DirectoryNotFoundException(msg); } RootDirectory = rootDirectory; RootName = rootName; UseRelativePaths = useRelativePaths; }
/// <summary> /// Deletes a given folder from the file system. /// </summary> /// <param name="virtualFolderPath">The qualified path of the folder to be created.</param> /// <exception cref="ArgumentNullException">If <paramref name="virtualFolderPath"/> /// is a null reference.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> /// <exception cref="VirtualResourceNotFoundException">If no file exists under the submitted /// <paramref name="virtualFolderPath"/>.</exception> public override void DeleteFolder(string virtualFolderPath) { string absolutePath; var folderInfo = GetFolderInfoInternal(virtualFolderPath, true, out absolutePath); //do not delete the root if (folderInfo.IsRootFolder) { VfsLog.Warn("Blocked attempt to delete root folder."); throw new ResourceAccessException(String.Format("Cannot delete root folder '{0}'", virtualFolderPath)); } DirectoryInfo dir = new DirectoryInfo(absolutePath); //do not delete drives if (dir.Parent == null) { VfsLog.Warn("Blocked attempt to delete drive by path [{0}]", virtualFolderPath); throw new ResourceAccessException("Cannot delete drive " + virtualFolderPath); } //delete the folder try { dir.Delete(true); } catch (Exception e) { VfsLog.Warn(e, "Error while trying to delete folder '{0}' from file system", virtualFolderPath); string msg = String.Format("Could not delete file '{0}'", virtualFolderPath); throw new ResourceAccessException(msg); } }
protected VirtualFileInfo GetFileInfoInternal(string virtualFilePath, bool mustExist, out string absolutePath) { try { if (String.IsNullOrEmpty(virtualFilePath)) { VfsLog.Debug("File request without file path received."); throw new ResourceAccessException("An empty or null string is not a valid file path"); } //make sure we operate on absolute paths absolutePath = PathUtil.GetAbsolutePath(virtualFilePath, RootDirectory); if (IsRootPath(absolutePath)) { VfsLog.Debug("Blocked file request with path '{0}' (resolves to root directory).", virtualFilePath); throw new ResourceAccessException("Invalid path submitted: " + virtualFilePath); } var fi = new FileInfo(absolutePath); VirtualFileInfo fileInfo = fi.CreateFileResourceInfo(); //convert to relative paths if required (also prevents qualified paths in validation exceptions) if (UseRelativePaths) { fileInfo.MakePathsRelativeTo(RootDirectory); } //make sure the user is allowed to access the resource ValidateResourceAccess(fileInfo); //verify file exists on FS if (mustExist) { fileInfo.VerifyFileExists(RootDirectory); } return(fileInfo); } catch (VfsException) { //just bubble internal exceptions throw; } catch (Exception e) { VfsLog.Debug(e, "Could not create file based on path '{0}' with root '{1}'", virtualFilePath, RootDirectory); throw new ResourceAccessException("Invalid path submitted: " + virtualFilePath); } }
/// <summary> /// Deletes a given file from the file system. /// </summary> /// <param name="virtualFilePath">The qualified path of the file to be created.</param> /// <exception cref="ArgumentNullException">If <paramref name="virtualFilePath"/> /// is a null reference.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> /// <exception cref="VirtualResourceNotFoundException">If no file exists under the submitted /// <paramref name="virtualFilePath"/>.</exception> public override void DeleteFile(string virtualFilePath) { string absolutePath; GetFileInfoInternal(virtualFilePath, true, out absolutePath); //delete the folder try { File.Delete(absolutePath); } catch (Exception e) { VfsLog.Warn(e, "Error while trying to delete file '{0}' from file system", virtualFilePath); string msg = String.Format("Could not delete file '{0}'", virtualFilePath); throw new ResourceAccessException(msg); } }
protected VirtualFolderInfo GetFolderInfoInternal(string virtualFolderPath, bool mustExist, out string absolutePath) { try { //make sure we operate on absolute paths absolutePath = PathUtil.GetAbsolutePath(virtualFolderPath ?? "", RootDirectory); if (IsRootPath(absolutePath)) { return(GetFileSystemRoot()); } var di = new DirectoryInfo(absolutePath); VirtualFolderInfo folderInfo = di.CreateFolderResourceInfo(); //convert to relative paths if required (also prevents qualified paths in validation exceptions) if (UseRelativePaths) { folderInfo.MakePathsRelativeTo(RootDirectory); } //make sure the user is allowed to access the resource ValidateResourceAccess(folderInfo); //verify folder exists on FS if (mustExist) { folderInfo.VerifyDirectoryExists(RootDirectory); } return(folderInfo); } catch (VfsException) { //just bubble internal exceptions throw; } catch (Exception e) { VfsLog.Debug(e, "Could not create directory based on path '{0}' with root '{1}'", virtualFolderPath, RootDirectory); throw new ResourceAccessException("Invalid path submitted: " + virtualFolderPath); } }
/// <summary> /// Resolves the parent folder for a virtual resource. /// </summary> /// <param name="child">The child file or folder.</param> /// <returns>The parent folder info.</returns> private VirtualFolderInfo GetParentInternal(VirtualResourceInfo child) { if (child == null) { throw new ArgumentNullException("child"); } string path = PathUtil.GetAbsolutePath(child.FullName, RootDirectory); if (IsRootPath(path)) { //only log the request for the root's parent - do not include that //info in an exception in case we're hiding path information string msg = "Error while requesting parent of resource {0} - the folder itself already is the root."; VfsLog.Error(msg, child.FullName); //create exception with a relative path, if required string exceptionPath = UseRelativePaths ? PathUtil.RelativeRootPrefix : child.FullName; msg = String.Format(msg, exceptionPath); throw new ResourceAccessException(msg); } //make sure the processed directory exists VirtualFolderInfo folder = child as VirtualFolderInfo; if (folder != null) { folder.VerifyDirectoryExists(RootDirectory); } else { VirtualFileInfo file = (VirtualFileInfo)child; file.VerifyFileExists(RootDirectory); } //get the path of the parent (returned value may be null!) string parentPath = Path.GetDirectoryName(path); //get folder info return(GetFolderInfo(parentPath)); }
/// <summary> /// Gets the binary contents as a stream in a blocking operation. /// Use the methods in <see cref="ContentUtil"/> class for simplified stream /// handling. /// </summary> /// <param name="virtualFilePath">The path of the file to be read.</param> /// <returns>A stream that allows the contents of the file to be read.</returns> /// <exception cref="ArgumentNullException">If <paramref name="virtualFilePath"/> /// is a null reference.</exception> /// <exception cref="VirtualResourceNotFoundException">If the file that is represented /// by <paramref name="virtualFilePath"/> does not exist in the file system.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> public override Stream ReadFileContents(string virtualFilePath) { if (virtualFilePath == null) { throw new ArgumentNullException("virtualFilePath"); } string absolutePath; GetFileInfoInternal(virtualFilePath, true, out absolutePath); try { return(File.OpenRead(absolutePath)); } catch (Exception e) { VfsLog.Error(e, "Could not open file [{0}] requested through virtual file path [{1}]", absolutePath, virtualFilePath); string msg = String.Format("An error occurred while attempting to open file [{0}].", virtualFilePath); throw new ResourceAccessException(msg, e); } }
/// <summary> /// Creates a new folder in the file system. /// </summary> /// <param name="virtualFolderPath">The qualified path of the folder to be created. parent folder, which /// needs to exists, and provide write access.</param> /// <returns>A <see cref="VirtualFileInfo"/> instance which represents /// the created folder.</returns> /// <exception cref="ArgumentNullException">If <paramref name="virtualFolderPath"/> /// is a null reference.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> /// <exception cref="VirtualResourceNotFoundException">If the parent folder /// does not exist.</exception> public override VirtualFolderInfo CreateFolder(string virtualFolderPath) { string parentPath, directoryName; try { string absolutePath; GetFolderInfoInternal(virtualFolderPath, false, out absolutePath); var dir = new DirectoryInfo(absolutePath); var parent = dir.Parent; parentPath = parent == null ? "" : parent.FullName; directoryName = dir.Name; } catch (Exception e) { string msg = String.Format("Submitted path [{0}] caused exception when trying to create folder.", virtualFolderPath); VfsLog.Debug(e, msg); throw new ResourceAccessException(msg, e); } return(CreateFolder(parentPath, directoryName)); }
private VirtualFolderInfo PerformFolderOperation(string virtualFolderPath, string destinationPath, FileOperation operation) { if (virtualFolderPath == null) { throw new ArgumentNullException("virtualFolderPath"); } if (destinationPath == null) { throw new ArgumentNullException("destinationPath"); } //get folder info for source and destination, thus validating the scope of both string absoluteSource; var sourceFolder = GetFolderInfoInternal(virtualFolderPath, true, out absoluteSource); string absoluteDestination; GetFolderInfoInternal(destinationPath, false, out absoluteDestination); string operationName = operation == FileOperation.Move ? "move" : "copy"; if (sourceFolder.IsRootFolder) { string msg = String.Format("Cannot {0} root folder (attempted destination: '{1}').", operationName, destinationPath); VfsLog.Debug(msg); throw new ResourceAccessException(msg); } if (String.Equals(absoluteSource, absoluteDestination, StringComparison.InvariantCultureIgnoreCase)) { string msg = String.Format("Cannot {0} folder to '{1}' - source and destination are the same.", operationName, destinationPath); VfsLog.Debug(msg); throw new ResourceAccessException(msg); } var sourceDir = new DirectoryInfo(absoluteSource); if (sourceDir.IsParentOf(absoluteDestination)) { string msg = String.Format("Cannot {0} folder '{1}' to '{2}' - destination folder is a child of the folder.", operationName, virtualFolderPath, destinationPath); VfsLog.Debug(msg); throw new ResourceAccessException(msg); } if (Directory.Exists(absoluteDestination)) { string msg = "Cannot {0} folder '{1}' to '{2}' - the destination folder already exists."; msg = String.Format(msg, operationName, virtualFolderPath, destinationPath); VfsLog.Debug(msg); throw new ResourceOverwriteException(msg); } try { switch (operation) { case FileOperation.Move: Directory.Move(absoluteSource, absoluteDestination); break; case FileOperation.Copy: PathUtil.CopyDirectory(absoluteSource, absoluteDestination, false); break; default: VfsLog.Fatal("Unsupported file operation received: {0}", operation); throw new ArgumentOutOfRangeException("operation"); } return(GetFolderInfo(absoluteDestination)); } catch (Exception e) { string msg = String.Format("An error occurred while trying to {0} directory '{1}' to '{2}'.", operationName, virtualFolderPath, destinationPath); VfsLog.Warn(e, msg); throw new ResourceAccessException(msg, e); } }
/// <summary> /// Creates a new folder in the file system. /// </summary> /// <param name="parentFolderPath">The qualified name of the designated parent folder, which /// needs to exists, and provide write access.</param> /// <param name="folderName">The name of the folder to be created.</param> /// <returns>A <see cref="VirtualFileInfo"/> instance which represents /// the created folder.</returns> /// <exception cref="ArgumentNullException">If <paramref name="parentFolderPath"/> /// is a null reference.</exception> /// <exception cref="ArgumentNullException">If <paramref name="folderName"/> /// is a null reference.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> /// <exception cref="VirtualResourceNotFoundException">If no folder exists that /// matches the submitted <paramref name="parentFolderPath"/>.</exception> /// <exception cref="ResourceOverwriteException">If the folder already exists on the file /// system.</exception> public override VirtualFolderInfo CreateFolder(string parentFolderPath, string folderName) { if (parentFolderPath == null) { throw new ArgumentNullException("parentFolderPath"); } if (folderName == null) { throw new ArgumentNullException("folderName"); } string absoluteParentPath; var parent = GetFolderInfoInternal(parentFolderPath, true, out absoluteParentPath); if (RootDirectory == null && parent.IsRootFolder) { VfsLog.Debug("Blocked attempt to create a folder '{0}' at system root.", folderName); throw new ResourceAccessException("Folders cannot be created at the system root."); } //create path of the child string childPath = PathUtil.GetAbsolutePath(folderName, new DirectoryInfo(absoluteParentPath)); //make sure the folder name is not a relative path that points outside the scope if (RootDirectory != null && !RootDirectory.IsParentOf(childPath)) { string msg = "Blocked attempt to create folder outside of root through with parent '{0}' and folder name '{1}'"; VfsLog.Warn(msg, absoluteParentPath, folderName); throw new ResourceAccessException("Invalid file path: " + folderName); } var directory = new DirectoryInfo(childPath); if (directory.Exists) { //log and create exception if the directory already exists VfsLog.Debug("Blocked attempt to recreate directory '{0}'", directory.FullName); string relativePath = PathUtil.GetRelativePath(childPath, RootDirectory); string msg = String.Format("The folder '{0}' already exists.", relativePath); throw new ResourceOverwriteException(msg); } try { //create directory directory.Create(); } catch (Exception e) { const string msg = "Exception occurred when trying to create new folder '{0}' for parent '{1}'"; VfsLog.Debug(e, msg, folderName, parent.FullName); throw new ResourceAccessException("Could not create folder", e); } var folder = directory.CreateFolderResourceInfo(); //adjust and return if (UseRelativePaths) { folder.MakePathsRelativeTo(RootDirectory); } return(folder); }
/// <summary> /// Creates or updates a given file resource in the file system. /// </summary> /// <param name="parentFolderPath">The qualified path of the parent folder that will /// contain the file.</param> /// <param name="fileName">The name of the file to be created.</param> /// <param name="input">A stream that provides the file's contents.</param> /// <param name="overwrite">Whether an existing file should be overwritten /// or not. If this parameter is false and the file already exists, a /// <see cref="ResourceOverwriteException"/> is thrown.</param> /// <exception cref="VirtualResourceNotFoundException">If the parent folder /// does not exist.</exception> /// <exception cref="ResourceAccessException">In case of invalid or prohibited /// resource access.</exception> /// <exception cref="ResourceOverwriteException">If a file already exists at the /// specified location, and the <paramref name="overwrite"/> flag was not set.</exception> /// <exception cref="ArgumentNullException">If any of the parameters is a null reference.</exception> public override VirtualFileInfo WriteFile(string parentFolderPath, string fileName, Stream input, bool overwrite) { if (parentFolderPath == null) { throw new ArgumentNullException("parentFolderPath"); } if (fileName == null) { throw new ArgumentNullException("fileName"); } if (input == null) { throw new ArgumentNullException("input"); } //get the parent and make sure it exists string absoluteParentPath; var parent = GetFolderInfoInternal(parentFolderPath, true, out absoluteParentPath); if (RootDirectory == null && parent.IsRootFolder) { VfsLog.Debug("Blocked attempt to create a file '{0}' at system root (which is the machine itself - no root directory was set).", fileName); throw new ResourceAccessException("Files cannot be created at the system root."); } //combine to file path and get virtual file (also makes sure we don't get out of scope) string absoluteFilePath = PathUtil.GetAbsolutePath(fileName, new DirectoryInfo(absoluteParentPath)); FileInfo fi = new FileInfo(absoluteFilePath); if (fi.Exists && !overwrite) { VfsLog.Debug("Blocked attempt to overwrite file '{0}'", fi.FullName); string msg = String.Format("The file [{0}] already exists.", fileName); throw new ResourceOverwriteException(msg); } try { using (Stream writeStream = new FileStream(fi.FullName, FileMode.Create, FileAccess.Write, FileShare.None)) { input.WriteTo(writeStream); } } catch (Exception e) { //log exception with full path string msg = "Could not write write submitted content to file '{0}'."; VfsLog.Error(e, msg, fi.FullName); //generate exception with relative path msg = String.Format(msg, PathUtil.GetRelativePath(fi.FullName, RootDirectory)); throw new ResourceAccessException(msg, e); } //return update file info var file = fi.CreateFileResourceInfo(); //adjust and return if (UseRelativePaths) { file.MakePathsRelativeTo(RootDirectory); } return(file); }
/// <summary> /// Validates whether a <see cref="LocalFileSystemProvider"/> was configured /// with access restricted to a given <see cref="LocalFileSystemProvider.RootDirectory"/>, /// and makes sure that the requested <paramref name="resource"/> is indeed contained /// within that folder. /// </summary> /// <param name="resource">The requested resource.</param> /// <exception cref="ResourceAccessException">If the requested resource is not /// a descendant of a configured <see cref="LocalFileSystemProvider.RootDirectory"/>.</exception> /// <exception cref="ArgumentNullException">If <paramref name="resource"/> /// is a null reference.</exception> private void ValidateResourceAccess(VirtualResourceInfo resource) { if (resource == null) { throw new ArgumentNullException("resource"); } //if there isn't a restricted custom root, every file resource can be accessed //(if the path is invalid, this will fail later, depending on the action) if (RootDirectory == null) { return; } try { string path = PathUtil.GetAbsolutePath(resource.FullName, RootDirectory); //if the root path was submitted, we're within the scope, too if (IsRootPath(path)) { return; } //if we have a custom root, make sure the resource is indeed a descendant of the root if (RootDirectory.IsParentOf(path)) { return; } } catch (ResourceAccessException e) { //just bubble a resource access exception if (e.Resource == null) { e.Resource = resource; } throw; } catch (Exception e) { //exceptions can happen in case of invalid file paths //log detailed info string error = "Resource request for '{0}' caused exception when validating against root directory '{1}'."; error = String.Format(error, resource.FullName, RootDirectory.FullName); VfsLog.Debug(e, error); //do not expose too much path information (e.g. absolute paths if disabled) error = String.Format("Invalid resource path: '{0}'.", resource.FullName); throw new ResourceAccessException(error) { Resource = resource }; } //if none of the above is true, the request is invalid //log detailed info string msg = "Resource request for '{0}' was blocked. The resource is outside the root directory '{1}'."; msg = String.Format(msg, resource.FullName, RootDirectory.FullName); VfsLog.Debug(msg); //do not expose too much path information (e.g. absolute paths if disabled) msg = String.Format("Invalid resource path: '{0}'.", resource.FullName); throw new ResourceAccessException(msg) { Resource = resource }; }