├── .gitignore ├── Explorer ├── App.config ├── App.xaml ├── App.xaml.cs ├── DirectoryItem.cs ├── Explorer.csproj ├── ImageConverter.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Licenses.licx │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Res │ ├── 698926-icon-93-inbox-upload-64.png │ ├── 698928-icon-95-folder-24.png │ ├── 699326-icon-54-document-24.png │ ├── delete-01.png │ ├── refresh-01.png │ └── rename-01.png ├── Settings.cs ├── ShellFileInfo.cs └── Utils.cs ├── README.md ├── VirtualFileSystem.sln └── VirtualFileSystem ├── Block.cs ├── DeviceAdapter ├── AbstractDevice.cs └── FileAdapter.cs ├── INode.cs ├── INodeDirectory.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs ├── Settings.settings └── licenses.licx ├── SuperBlock.cs ├── Utils.cs ├── VFS.Directory.cs ├── VFS.File.cs ├── VFS.cs ├── VFSCore.cs ├── VirtualFileSystem.csproj ├── WeakValueDictionary.cs └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | packages/ 3 | .DS_Store 4 | bin/ 5 | obj/ 6 | -------------------------------------------------------------------------------- /Explorer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Explorer/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Explorer/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Explorer 10 | { 11 | /// 12 | /// App.xaml 的交互逻辑 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Explorer/DirectoryItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using VirtualFileSystem; 3 | using System.Drawing; 4 | 5 | namespace Explorer 6 | { 7 | public class DirectoryItem 8 | { 9 | public String size { get; set; } 10 | 11 | public String name { get; set; } 12 | 13 | public String path { get; set; } 14 | 15 | public String extension { get; set; } 16 | 17 | public DateTime modifyTime { get; set; } 18 | 19 | public DateTime creationTime { get; set; } 20 | 21 | public Bitmap icon { get; set; } 22 | 23 | public Boolean isDirectory { get; set; } 24 | 25 | public UInt32 flag { get; set; } 26 | 27 | public UInt32 owner { get; set; } 28 | 29 | public UInt32 inodeIndex { get; set; } 30 | 31 | public UInt32 blockPreserved { get; set; } 32 | 33 | public UInt32 refCount { get; set; } 34 | 35 | public DirectoryItem(VFS.DirectoryInfo info) 36 | { 37 | this.modifyTime = new DateTime((long)info.modifyTime); 38 | this.creationTime = new DateTime((long)info.creationTime); 39 | this.name = info.name; 40 | this.path = info.path; 41 | this.isDirectory = info.isDirectory; 42 | this.flag = info.flags; 43 | this.owner = info.owner; 44 | this.inodeIndex = info.inodeIndex; 45 | this.blockPreserved = info.inode.blockPreserved; 46 | this.refCount = info.inode.linkCount; 47 | 48 | if (info.isDirectory) 49 | { 50 | this.extension = "文件夹"; 51 | this.icon = ShellFileInfo.GetFolderIcon(ShellFileInfo.IconSize.Small, ShellFileInfo.FolderType.Closed); 52 | this.size = ""; 53 | } 54 | else 55 | { 56 | this.extension = ShellFileInfo.GetFileTypeDescription(this.name); 57 | this.icon = ShellFileInfo.GetFileIcon(this.name, ShellFileInfo.IconSize.Small, false); 58 | this.size = Utils.FormatSize(info.size); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Explorer/Explorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {04CADF34-E149-4041-A344-75046CBF870A} 8 | WinExe 9 | Properties 10 | Explorer 11 | Explorer 12 | v4.0 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | true 40 | bin\x86\Debug\ 41 | DEBUG;TRACE 42 | full 43 | x86 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | true 47 | 48 | 49 | bin\x86\Release\ 50 | TRACE 51 | true 52 | pdbonly 53 | x86 54 | prompt 55 | MinimumRecommendedRules.ruleset 56 | true 57 | 58 | 59 | 60 | 61 | True 62 | 63 | 64 | True 65 | 66 | 67 | True 68 | 69 | 70 | 71 | 72 | True 73 | 74 | 75 | True 76 | 77 | 78 | True 79 | 80 | 81 | True 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 4.0 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | MSBuild:Compile 107 | Designer 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | MSBuild:Compile 116 | Designer 117 | 118 | 119 | App.xaml 120 | Code 121 | 122 | 123 | MainWindow.xaml 124 | Code 125 | 126 | 127 | 128 | 129 | Code 130 | 131 | 132 | True 133 | True 134 | Resources.resx 135 | Always 136 | 137 | 138 | True 139 | Settings.settings 140 | True 141 | 142 | 143 | 144 | ResXFileCodeGenerator 145 | Resources.Designer.cs 146 | Always 147 | 148 | 149 | SettingsSingleFileGenerator 150 | Settings.Designer.cs 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | {362e0e54-3b00-4c19-8f58-90f8e5430f95} 160 | VirtualFileSystem 161 | 162 | 163 | 164 | 165 | Always 166 | 167 | 168 | 169 | 170 | Always 171 | 172 | 173 | 174 | 175 | Always 176 | 177 | 178 | 179 | 180 | Always 181 | 182 | 183 | 184 | 185 | Always 186 | 187 | 188 | 189 | 190 | Always 191 | 192 | 193 | 194 | 201 | -------------------------------------------------------------------------------- /Explorer/ImageConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Drawing; 7 | using System.IO; 8 | using System.Windows.Data; 9 | using System.Drawing.Imaging; 10 | using System.Windows.Media.Imaging; 11 | 12 | namespace Explorer 13 | { 14 | public class ImageConverter : IValueConverter 15 | { 16 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 17 | { 18 | if (value is Bitmap) 19 | { 20 | var stream = new MemoryStream(); 21 | ((Bitmap)value).Save(stream, ImageFormat.Png); 22 | 23 | BitmapImage bitmap = new BitmapImage(); 24 | bitmap.BeginInit(); 25 | bitmap.StreamSource = stream; 26 | bitmap.EndInit(); 27 | 28 | return bitmap; 29 | } 30 | return value; 31 | } 32 | 33 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Explorer/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 114 | 115 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 磁盘类型: 243 | 内存映射文件 244 | 245 | 246 | 磁盘容量: 247 | 0 248 | 249 | 250 | 区块大小: 251 | 0 K 252 | 253 | 254 | 255 | 256 | 257 | inode 总数: 258 | 0 259 | 260 | 261 | inode 已分配: 262 | 0 263 | 264 | 265 | 266 | 267 | 268 | 区块总数: 269 | 0 270 | 271 | 272 | 区块已分配: 273 | 0 274 | 275 | 276 | 区块已保留: 277 | 0 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 310 | 326 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | -------------------------------------------------------------------------------- /Explorer/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using VirtualFileSystem; 16 | using VirtualFileSystem.DeviceAdapter; 17 | using System.Configuration; 18 | using Infragistics.Controls.Interactions; 19 | using System.Windows.Media.Effects; 20 | 21 | namespace Explorer 22 | { 23 | /// 24 | /// MainWindow.xaml 的交互逻辑 25 | /// 26 | public partial class MainWindow : Window 27 | { 28 | AbstractDevice device; 29 | VFS vfs; 30 | 31 | String currentDirectory = "/"; 32 | 33 | public MainWindow() 34 | { 35 | InitializeComponent(); 36 | } 37 | 38 | private void Reload() 39 | { 40 | LoadDirectory(currentDirectory); 41 | UpdateInfo(); 42 | } 43 | 44 | private void GoUp() 45 | { 46 | if (currentDirectory.EndsWith("/") && currentDirectory.Length > 1) 47 | { 48 | currentDirectory = currentDirectory.Substring(0, currentDirectory.Length - 1); 49 | } 50 | var pos = currentDirectory.LastIndexOf("/"); 51 | currentDirectory = currentDirectory.Substring(0, pos + 1); 52 | if (LoadDirectory(currentDirectory)) 53 | { 54 | history.Add(currentDirectory); 55 | historyNeedle++; 56 | buttonForward.IsEnabled = (historyNeedle + 1 < history.Count); 57 | buttonBack.IsEnabled = (historyNeedle - 1 >= 0); 58 | } 59 | } 60 | 61 | private Boolean LoadDirectory(String directory) 62 | { 63 | if (!directory.StartsWith("/")) 64 | { 65 | System.Windows.Forms.MessageBox.Show("您输入的路径无效!", "VFS", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); 66 | return false; 67 | } 68 | if (!directory.EndsWith("/")) 69 | { 70 | directory += "/"; 71 | } 72 | currentDirectory = directory; 73 | textboxPath.Text = directory; 74 | 75 | try 76 | { 77 | var dir = vfs.NewDirectory(directory); 78 | var list = dir.List(); 79 | listView.Items.Clear(); 80 | foreach (var info in list) 81 | { 82 | listView.Items.Add(new DirectoryItem(info)); 83 | } 84 | } 85 | catch (Exception e) 86 | { 87 | System.Windows.Forms.MessageBox.Show(e.Message, "VFS", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); 88 | return false; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | private void UpdateInfo() 95 | { 96 | labelDeviceSize.Caption = Utils.FormatSize(vfs.GetDevice().Size()); 97 | labelBlockSize.Caption = Utils.FormatSize(vfs.GetSuperBlock().blockSize); 98 | labelINodeCapacity.Caption = vfs.GetSuperBlock().inodeCapacity.ToString(); 99 | labelINodeAllocated.Caption = vfs.GetSuperBlock().inodeAllocated.ToString(); 100 | labelBlockCapacity.Caption = vfs.GetSuperBlock().blockCapacity.ToString(); 101 | labelBlockAllocated.Caption = vfs.GetSuperBlock().blockAllocated.ToString(); 102 | labelBlockPreserved.Caption = vfs.GetSuperBlock().blockPreserved.ToString(); 103 | } 104 | 105 | private void Window_Loaded(object sender, RoutedEventArgs e) 106 | { 107 | if (Properties.Settings.Default["vfsfile"] == null || ((String)(Properties.Settings.Default["vfsfile"])).Length == 0) 108 | { 109 | var r = System.Windows.Forms.MessageBox.Show("首次启动 VFS 需要在磁盘上建立一个虚拟磁盘镜像文件,镜像文件大小为 1GB。\n点击确定后请选择一个可以写入的位置来建立此文件,点击取消关闭程序。", "磁盘镜像文件未找到", System.Windows.Forms.MessageBoxButtons.OKCancel, System.Windows.Forms.MessageBoxIcon.Information); 110 | if (r == System.Windows.Forms.DialogResult.Cancel) 111 | { 112 | Close(); 113 | return; 114 | } 115 | 116 | using (var dialog = new System.Windows.Forms.SaveFileDialog()) 117 | { 118 | dialog.Title = "保存磁盘镜像文件"; 119 | dialog.FileName = "vfs.bin"; 120 | var result = dialog.ShowDialog(); 121 | if (result == System.Windows.Forms.DialogResult.Cancel) 122 | { 123 | Close(); 124 | return; 125 | } 126 | else 127 | { 128 | try 129 | { 130 | device = new FileAdapter(dialog.FileName, 1 << 10 << 10 << 10); 131 | } 132 | catch (Exception ex) 133 | { 134 | System.Windows.Forms.MessageBox.Show("打开镜像文件失败,可能您没有权限在该位置写入文件,或您内存不足。程序即将退出,请重新打开程序。"); 135 | Close(); 136 | return; 137 | } 138 | // 更新配置 139 | Properties.Settings.Default["vfsfile"] = dialog.FileName; 140 | Properties.Settings.Default.Save(); 141 | } 142 | } 143 | } 144 | else 145 | { 146 | device = new FileAdapter((String)Properties.Settings.Default["vfsfile"], 1 << 10 << 10 << 10); 147 | } 148 | 149 | vfs = new VFS(device); 150 | if (!vfs.IsFormated()) 151 | { 152 | var result = System.Windows.Forms.MessageBox.Show("该磁盘镜像文件尚未格式化,是否格式化?", "VFS", System.Windows.Forms.MessageBoxButtons.YesNo); 153 | if (result == System.Windows.Forms.DialogResult.No) 154 | { 155 | Close(); 156 | return; 157 | } 158 | 159 | vfs.Format(device.Size() >> 10, 4); 160 | 161 | // 写入一个示例文件 162 | var file = vfs.NewFile("/README.txt", VFS.FileMode.Create); 163 | file.Write(Encoding.UTF8.GetBytes("Hello world!\r\n\r\n这个文件是格式化时自动建立的,如果你看到了这个文件,说明一切工作正常。\r\n\r\n当你使用记事本修改这个文件并保存以后,它会同步到虚拟文件系统中。\r\n\r\n该虚拟文件系统是索引文件系统,inode 默认占总空间的 10%,理论可支持单一文件最大 2GB。")); 164 | 165 | System.Windows.Forms.MessageBox.Show("磁盘格式化成功!", "VFS", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information); 166 | } 167 | 168 | LoadDirectory("/"); 169 | 170 | UpdateInfo(); 171 | } 172 | 173 | private void ButtonRefresh_Click(object sender, RoutedEventArgs e) 174 | { 175 | UpdateInfo(); 176 | } 177 | 178 | private XamDialogWindow CreateDialogWindow(double height = 100) 179 | { 180 | return new XamDialogWindow() { 181 | Width = 350, Height = height, 182 | StartupPosition = StartupPosition.Center, 183 | CloseButtonVisibility = Visibility.Collapsed 184 | }; 185 | } 186 | 187 | private void listView_MouseDoubleClick(object sender, MouseButtonEventArgs e) 188 | { 189 | const int bufferSize = 4096; 190 | 191 | var item = (DirectoryItem)listView.SelectedItem; 192 | 193 | if (item == null) 194 | { 195 | return; 196 | } 197 | 198 | if (item.isDirectory) 199 | { 200 | if (item.name == ".") 201 | { 202 | Reload(); 203 | return; 204 | } 205 | if (item.name == "..") 206 | { 207 | GoUp(); 208 | return; 209 | } 210 | 211 | if (history.Count - historyNeedle - 1 > 0) 212 | { 213 | history.RemoveRange(historyNeedle + 1, history.Count - historyNeedle - 1); 214 | } 215 | var newDir = currentDirectory + item.name + "/"; 216 | if (LoadDirectory(newDir)) 217 | { 218 | history.Add(newDir); 219 | historyNeedle++; 220 | buttonForward.IsEnabled = (historyNeedle + 1 < history.Count); 221 | buttonBack.IsEnabled = (historyNeedle - 1 >= 0); 222 | } 223 | } 224 | else 225 | { 226 | // 读取文件内容到临时变量 227 | String tempFileName = System.IO.Path.GetTempFileName() + System.IO.Path.GetExtension(item.path); 228 | using (System.IO.BinaryWriter writer = new System.IO.BinaryWriter(new System.IO.FileStream(tempFileName, System.IO.FileMode.Create))) 229 | { 230 | var file = vfs.NewFile(item.path, VFS.FileMode.Open); 231 | byte[] buffer = new byte[bufferSize]; 232 | int count; 233 | while ((count = (int)file.Read(buffer, 0, (uint)buffer.Length)) != 0) 234 | { 235 | writer.Write(buffer, 0, count); 236 | } 237 | } 238 | 239 | FadeOutWindow(); 240 | 241 | XamDialogWindow win = CreateDialogWindow(); 242 | win.Header = "VFS"; 243 | 244 | Label label = new Label(); 245 | label.Content = "正在等待应用程序关闭, 文件内容将在程序关闭后自动更新..."; 246 | label.VerticalContentAlignment = VerticalAlignment.Center; 247 | label.HorizontalAlignment = HorizontalAlignment.Center; 248 | 249 | win.Content = label; 250 | win.IsModal = true; 251 | windowContainer.Children.Add(win); 252 | 253 | Utils.ProcessUITasks(); 254 | 255 | // 调用系统默认程序打开 256 | var process = new System.Diagnostics.Process(); 257 | process.StartInfo = new System.Diagnostics.ProcessStartInfo(tempFileName); 258 | process.EnableRaisingEvents = true; 259 | process.Start(); 260 | 261 | try 262 | { 263 | process.WaitForExit(); 264 | } 265 | catch (Exception ex) 266 | { 267 | } 268 | 269 | // 在关闭后,读取内容写回文件系统 270 | using (System.IO.BinaryReader reader = new System.IO.BinaryReader(new System.IO.FileStream(tempFileName, System.IO.FileMode.Open))) 271 | { 272 | var file = vfs.NewFile(item.path, VFS.FileMode.Create); 273 | byte[] buffer = new byte[bufferSize]; 274 | int count; 275 | while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) 276 | { 277 | file.Write(buffer, 0, (uint)count); 278 | } 279 | } 280 | 281 | win.Close(); 282 | windowContainer.Children.Remove(win); 283 | FadeInWindow(); 284 | 285 | Reload(); 286 | UpdateInfo(); 287 | } 288 | } 289 | 290 | private void FadeOutWindow(Boolean blur = true) 291 | { 292 | if (blur) 293 | { 294 | var blurEffect = new BlurEffect() { Radius = 10 }; 295 | gridContent.Effect = blurEffect; 296 | } 297 | gridContent.Opacity = 0.5; 298 | } 299 | 300 | private void FadeInWindow() 301 | { 302 | gridContent.Opacity = 1; 303 | gridContent.Effect = null; 304 | } 305 | 306 | private void buttonUploadLocal_Click(object sender, RoutedEventArgs e) 307 | { 308 | const int bufferSize = 4096; 309 | 310 | var dialog = new System.Windows.Forms.OpenFileDialog(); 311 | dialog.Title = "选择文件"; 312 | var result = dialog.ShowDialog(); 313 | if (result == System.Windows.Forms.DialogResult.Cancel) 314 | { 315 | return; 316 | } 317 | 318 | VFS.Directory dir = vfs.NewDirectory(currentDirectory); 319 | if (dir.Contains(dialog.SafeFileName)) 320 | { 321 | System.Windows.Forms.MessageBox.Show("文件夹下已存在同名文件或文件夹", "添加失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 322 | return; 323 | } 324 | 325 | try 326 | { 327 | // 写入文件 328 | using (var reader = new System.IO.BinaryReader(new System.IO.FileStream(dialog.FileName, System.IO.FileMode.Open))) 329 | { 330 | var file = vfs.NewFile(currentDirectory + dialog.SafeFileName, VFS.FileMode.Create); 331 | byte[] buffer = new byte[bufferSize]; 332 | int count; 333 | while ((count = reader.Read(buffer, 0, buffer.Length)) != 0) 334 | { 335 | file.Write(buffer, 0, (uint)count); 336 | } 337 | } 338 | } 339 | catch (Exception ex) 340 | { 341 | System.Windows.Forms.MessageBox.Show("添加文件失败 :-(", "添加失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 342 | return; 343 | } 344 | 345 | Reload(); 346 | } 347 | 348 | private void buttonNewFolder_Click(object sender, RoutedEventArgs e) 349 | { 350 | XamDialogWindow win = CreateDialogWindow(150); 351 | win.Header = "创建文件夹"; 352 | 353 | StackPanel stack = new StackPanel(); 354 | stack.VerticalAlignment = VerticalAlignment.Center; 355 | stack.Margin = new Thickness(10); 356 | 357 | Label label = new Label(); 358 | label.Content = "请输入文件夹名字"; 359 | stack.Children.Add(label); 360 | 361 | TextBox txtName = new TextBox(); 362 | txtName.Text = "新建文件夹"; 363 | stack.Children.Add(txtName); 364 | 365 | StackPanel stackButton = new StackPanel(); 366 | stackButton.Orientation = Orientation.Horizontal; 367 | stackButton.HorizontalAlignment = HorizontalAlignment.Center; 368 | stack.Children.Add(stackButton); 369 | 370 | Button btnOK = new Button(); 371 | btnOK.Content = "创建"; 372 | btnOK.Padding = new Thickness(10, 0, 10, 0); 373 | btnOK.Margin = new Thickness(10); 374 | stackButton.Children.Add(btnOK); 375 | Button btnCancel = new Button(); 376 | btnCancel.Content = "取消"; 377 | btnCancel.Padding = new Thickness(10, 0, 10, 0); 378 | btnCancel.Margin = new Thickness(10); 379 | stackButton.Children.Add(btnCancel); 380 | 381 | FadeOutWindow(); 382 | 383 | win.Content = stack; 384 | win.IsModal = true; 385 | windowContainer.Children.Add(win); 386 | 387 | txtName.Focus(); 388 | 389 | btnOK.Click += delegate 390 | { 391 | VFS.Directory dir = vfs.NewDirectory(currentDirectory); 392 | if (dir.Contains(txtName.Text)) { 393 | System.Windows.Forms.MessageBox.Show("该文件或文件夹已存在", "创建失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 394 | txtName.Focus(); 395 | return; 396 | } 397 | try 398 | { 399 | dir.CreateDirectory(txtName.Text); 400 | } 401 | catch (Exception ex) 402 | { 403 | System.Windows.Forms.MessageBox.Show(ex.Message, "创建失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 404 | txtName.Focus(); 405 | return; 406 | } 407 | win.Close(); 408 | windowContainer.Children.Remove(win); 409 | FadeInWindow(); 410 | Reload(); 411 | }; 412 | 413 | btnCancel.Click += delegate 414 | { 415 | win.Close(); 416 | windowContainer.Children.Remove(win); 417 | FadeInWindow(); 418 | }; 419 | } 420 | 421 | private void buttonNewFile_Click(object sender, RoutedEventArgs e) 422 | { 423 | XamDialogWindow win = CreateDialogWindow(150); 424 | win.Header = "创建文件"; 425 | 426 | StackPanel stack = new StackPanel(); 427 | stack.VerticalAlignment = VerticalAlignment.Center; 428 | stack.Margin = new Thickness(10); 429 | 430 | Label label = new Label(); 431 | label.Content = "请输入文件名"; 432 | stack.Children.Add(label); 433 | 434 | TextBox txtName = new TextBox(); 435 | txtName.Text = "文本文件.txt"; 436 | stack.Children.Add(txtName); 437 | 438 | StackPanel stackButton = new StackPanel(); 439 | stackButton.Orientation = Orientation.Horizontal; 440 | stackButton.HorizontalAlignment = HorizontalAlignment.Center; 441 | stack.Children.Add(stackButton); 442 | 443 | Button btnOK = new Button(); 444 | btnOK.Content = "创建"; 445 | btnOK.Padding = new Thickness(10, 0, 10, 0); 446 | btnOK.Margin = new Thickness(10); 447 | stackButton.Children.Add(btnOK); 448 | Button btnCancel = new Button(); 449 | btnCancel.Content = "取消"; 450 | btnCancel.Padding = new Thickness(10, 0, 10, 0); 451 | btnCancel.Margin = new Thickness(10); 452 | stackButton.Children.Add(btnCancel); 453 | 454 | FadeOutWindow(); 455 | 456 | win.Content = stack; 457 | win.IsModal = true; 458 | windowContainer.Children.Add(win); 459 | 460 | txtName.Focus(); 461 | 462 | btnOK.Click += delegate 463 | { 464 | VFS.Directory dir = vfs.NewDirectory(currentDirectory); 465 | if (dir.Contains(txtName.Text)) 466 | { 467 | System.Windows.Forms.MessageBox.Show("该文件或文件夹已存在", "创建失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 468 | txtName.Focus(); 469 | return; 470 | } 471 | try 472 | { 473 | VFS.File file = vfs.NewFile(currentDirectory + txtName.Text, VFS.FileMode.CreateNew); 474 | } 475 | catch (Exception ex) 476 | { 477 | System.Windows.Forms.MessageBox.Show(ex.Message, "创建失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 478 | txtName.Focus(); 479 | return; 480 | } 481 | win.Close(); 482 | windowContainer.Children.Remove(win); 483 | FadeInWindow(); 484 | Reload(); 485 | }; 486 | 487 | btnCancel.Click += delegate 488 | { 489 | win.Close(); 490 | windowContainer.Children.Remove(win); 491 | FadeInWindow(); 492 | }; 493 | } 494 | 495 | private void buttonDelete_Click(object sender, RoutedEventArgs e) 496 | { 497 | var item = (DirectoryItem)listView.SelectedItem; 498 | 499 | if (item == null) 500 | { 501 | return; 502 | } 503 | 504 | XamDialogWindow win = CreateDialogWindow(150); 505 | win.Header = "删除"; 506 | 507 | StackPanel stack = new StackPanel(); 508 | stack.VerticalAlignment = VerticalAlignment.Center; 509 | stack.Margin = new Thickness(10); 510 | 511 | Label label = new Label(); 512 | label.Content = "您确认要删除该" + (item.isDirectory ? "文件夹" : "文件") + "吗?"; 513 | stack.Children.Add(label); 514 | 515 | StackPanel stackButton = new StackPanel(); 516 | stackButton.Orientation = Orientation.Horizontal; 517 | stackButton.HorizontalAlignment = HorizontalAlignment.Center; 518 | stack.Children.Add(stackButton); 519 | 520 | Button btnOK = new Button(); 521 | btnOK.Content = "删除"; 522 | btnOK.Padding = new Thickness(10, 0, 10, 0); 523 | btnOK.Margin = new Thickness(10); 524 | stackButton.Children.Add(btnOK); 525 | Button btnCancel = new Button(); 526 | btnCancel.Content = "取消"; 527 | btnCancel.Padding = new Thickness(10, 0, 10, 0); 528 | btnCancel.Margin = new Thickness(10); 529 | stackButton.Children.Add(btnCancel); 530 | 531 | FadeOutWindow(); 532 | 533 | win.Content = stack; 534 | win.IsModal = true; 535 | windowContainer.Children.Add(win); 536 | 537 | btnOK.Click += delegate 538 | { 539 | VFS.Directory dir = vfs.NewDirectory(currentDirectory); 540 | 541 | try 542 | { 543 | dir.Delete(item.name); 544 | } 545 | catch (Exception ex) 546 | { 547 | System.Windows.Forms.MessageBox.Show(ex.Message, "删除失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 548 | return; 549 | } 550 | win.Close(); 551 | windowContainer.Children.Remove(win); 552 | FadeInWindow(); 553 | Reload(); 554 | }; 555 | 556 | btnCancel.Click += delegate 557 | { 558 | win.Close(); 559 | windowContainer.Children.Remove(win); 560 | FadeInWindow(); 561 | }; 562 | } 563 | 564 | private void buttonRename_Click(object sender, RoutedEventArgs e) 565 | { 566 | var item = (DirectoryItem)listView.SelectedItem; 567 | 568 | if (item == null) 569 | { 570 | return; 571 | } 572 | 573 | XamDialogWindow win = CreateDialogWindow(150); 574 | win.Header = "重命名"; 575 | 576 | StackPanel stack = new StackPanel(); 577 | stack.VerticalAlignment = VerticalAlignment.Center; 578 | stack.Margin = new Thickness(10); 579 | 580 | Label label = new Label(); 581 | label.Content = "请输入新文件" + (item.isDirectory ? "夹" : "") + "名"; 582 | stack.Children.Add(label); 583 | 584 | TextBox txtName = new TextBox(); 585 | txtName.Text = item.name; 586 | stack.Children.Add(txtName); 587 | 588 | StackPanel stackButton = new StackPanel(); 589 | stackButton.Orientation = Orientation.Horizontal; 590 | stackButton.HorizontalAlignment = HorizontalAlignment.Center; 591 | stack.Children.Add(stackButton); 592 | 593 | Button btnOK = new Button(); 594 | btnOK.Content = "重命名"; 595 | btnOK.Padding = new Thickness(10, 0, 10, 0); 596 | btnOK.Margin = new Thickness(10); 597 | stackButton.Children.Add(btnOK); 598 | Button btnCancel = new Button(); 599 | btnCancel.Content = "取消"; 600 | btnCancel.Padding = new Thickness(10, 0, 10, 0); 601 | btnCancel.Margin = new Thickness(10); 602 | stackButton.Children.Add(btnCancel); 603 | 604 | FadeOutWindow(); 605 | 606 | win.Content = stack; 607 | win.IsModal = true; 608 | windowContainer.Children.Add(win); 609 | 610 | txtName.Focus(); 611 | 612 | btnOK.Click += delegate 613 | { 614 | VFS.Directory dir = vfs.NewDirectory(currentDirectory); 615 | if (dir.Contains(txtName.Text)) 616 | { 617 | System.Windows.Forms.MessageBox.Show("新文件或文件夹名已存在", "重命名失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 618 | txtName.Focus(); 619 | return; 620 | } 621 | try 622 | { 623 | dir.Rename(item.name, txtName.Text); 624 | } 625 | catch (Exception ex) 626 | { 627 | System.Windows.Forms.MessageBox.Show(ex.Message, "重命名失败", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error); 628 | txtName.Focus(); 629 | return; 630 | } 631 | win.Close(); 632 | windowContainer.Children.Remove(win); 633 | FadeInWindow(); 634 | Reload(); 635 | }; 636 | 637 | btnCancel.Click += delegate 638 | { 639 | win.Close(); 640 | windowContainer.Children.Remove(win); 641 | FadeInWindow(); 642 | }; 643 | } 644 | 645 | List history = new List() { "/" }; 646 | int historyNeedle = 0; 647 | 648 | private void buttonBack_Click(object sender, RoutedEventArgs e) 649 | { 650 | if (historyNeedle - 1 >= 0) 651 | { 652 | historyNeedle--; 653 | LoadDirectory(history[historyNeedle]); 654 | buttonForward.IsEnabled = (historyNeedle + 1 < history.Count); 655 | buttonBack.IsEnabled = (historyNeedle - 1 >= 0); 656 | } 657 | } 658 | 659 | private void buttonForward_Click(object sender, RoutedEventArgs e) 660 | { 661 | if (historyNeedle + 1 < history.Count) 662 | { 663 | historyNeedle++; 664 | LoadDirectory(history[historyNeedle]); 665 | buttonForward.IsEnabled = (historyNeedle + 1 < history.Count); 666 | buttonBack.IsEnabled = (historyNeedle - 1 >= 0); 667 | } 668 | } 669 | 670 | private void textboxPath_KeyDown(object sender, KeyEventArgs e) 671 | { 672 | if (e.Key == Key.Enter) 673 | { 674 | if (history.Count - historyNeedle - 1 > 0) 675 | { 676 | history.RemoveRange(historyNeedle + 1, history.Count - historyNeedle - 1); 677 | } 678 | var dir = textboxPath.Text; 679 | if (LoadDirectory(dir)) 680 | { 681 | history.Add(dir); 682 | historyNeedle++; 683 | buttonForward.IsEnabled = (historyNeedle + 1 < history.Count); 684 | buttonBack.IsEnabled = (historyNeedle - 1 >= 0); 685 | } 686 | } 687 | } 688 | 689 | private void buttonUp_Click(object sender, RoutedEventArgs e) 690 | { 691 | GoUp(); 692 | } 693 | } 694 | } 695 | -------------------------------------------------------------------------------- /Explorer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的常规信息通过以下 8 | // 特性集控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("Explorer")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Explorer")] 15 | [assembly: AssemblyCopyright("Copyright © 2015")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 使此程序集中的类型 20 | // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 21 | // 则将该类型上的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请在 25 | // 中的 .csproj 文件中 26 | //设置 CultureYouAreCodingWith。 例如,如果您在源文件中 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(在页面或应用程序资源词典中 37 | // 未找到某个资源的情况下使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(在页面、应用程序或任何主题特定资源词典中 40 | // 未找到某个资源的情况下使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下面四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 52 | // 方法是按如下所示使用“*”: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Explorer/Properties/Licenses.licx: -------------------------------------------------------------------------------- 1 | Infragistics.Windows.Ribbon.XamRibbon, InfragisticsWPF4.Ribbon.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 2 | Infragistics.Windows.DataPresenter.XamDataPresenter, InfragisticsWPF4.DataPresenter.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 3 | Infragistics.Controls.Interactions.XamDialogWindow, InfragisticsWPF4.Controls.Interactions.XamDialogWindow.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 4 | Infragistics.Controls.Menus.XamMenu, InfragisticsWPF4.Controls.Menus.XamMenu.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 5 | Infragistics.Controls.Menus.XamContextMenu, InfragisticsWPF4.Controls.Menus.XamMenu.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 6 | Infragistics.Controls.XamDock, InfragisticsWPF4.DataVisualization.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 7 | Infragistics.Windows.DockManager.XamDockManager, InfragisticsWPF4.DockManager.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 8 | Infragistics.Controls.Charts.XamPieChart, InfragisticsWPF4.Controls.Charts.XamDataChart.v15.1, Version=15.1.20151.1000, Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb 9 | -------------------------------------------------------------------------------- /Explorer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Explorer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Explorer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 使用此强类型资源类,为所有资源查找 51 | /// 重写当前线程的 CurrentUICulture 属性。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 65 | /// 66 | internal static System.Drawing.Bitmap _698926_icon_93_inbox_upload_64 { 67 | get { 68 | object obj = ResourceManager.GetObject("_698926_icon_93_inbox_upload_64", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 75 | /// 76 | internal static System.Drawing.Bitmap _698928_icon_95_folder_24 { 77 | get { 78 | object obj = ResourceManager.GetObject("_698928_icon_95_folder_24", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 85 | /// 86 | internal static System.Drawing.Bitmap _699326_icon_54_document_24 { 87 | get { 88 | object obj = ResourceManager.GetObject("_699326_icon_54_document_24", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 95 | /// 96 | internal static System.Drawing.Bitmap delete_01 { 97 | get { 98 | object obj = ResourceManager.GetObject("delete_01", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 105 | /// 106 | internal static System.Drawing.Bitmap refresh_01 { 107 | get { 108 | object obj = ResourceManager.GetObject("refresh_01", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。 115 | /// 116 | internal static System.Drawing.Bitmap rename_01 { 117 | get { 118 | object obj = ResourceManager.GetObject("rename_01", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Explorer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | 123 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 124 | YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8 125 | AAADGElEQVR4XuWa3WmcMRREU5JL8GMe3UDAJaQEl+ASXIo7SAspIR1sZuAqbMSRVr9fCHo4sMwn3Zm5 126 | 4DW77Jfb7XY0KJ4EiieB4kmgeBIongSKJ4HiSaB4EiieBIot/Pj29Um8iRd6fgXyfhWf4p2et4DiI2To 127 | 8r/ELXilczuR5/Odv/mgc49AsYaM8vKJy5Ygr1KG7iWgWEIGJePE9iXI41GGriWgSGjwI+PEtiVodmuG 128 | 5iWgmKOBrcaJ5UvQzN4MTW+MKOZomN/tyaTGsiVoVm9580mzclDM0bCRAGZ6CZox4u3zTzQvB0XCA2Mw 129 | GdYYXoLubi1vUCzhwWFAxjW6l6A728sbFGvYIIwoQI3mJejsJeUNio+wURhSkBoPl6Azl5U3KLZgwzCm 130 | QDWeaZ7Rs0vLGxRbsXEEoGAlMLC1eEZ3SkyVNyj24AARhAKW+Cu4X4dGZ0tMlzco9uIgEYiClvD5l+Cf 131 | lDcojuBAEYwCr2RZeYPiKA4WASn4CpaWNyjO4IARlArMsLy8QXEWB43AVGSELeUNiitw4AhOhXrYVt6g 132 | uAoHjwJUrIWt5Q2KK1EB/5ujci1s/8YZxZW4RFaqh/97ASpw7p+Ag0cBKtbD1iWgOIsDR3AqNIJnFT9F 133 | zoDiDAq6uvw9098x5qA4igLuLJ9YugQUR1CwK8onli0BxV4UaKS8z49+HDZLloBiDwoyWv7PO7tfh0Zn 134 | a0wvAcVWFGC6/OQsM7UEFFuQ8bLyCT+LM3S3xvASUHyEDJeXT/hMnKUZNYaWgGINGW0rn/DZuEOzanQv 135 | AcUSMthePuE7cZdm1uhaAoqEBvs3OZeUT/huzKDZNZqXgGKOB2YGLUyVT3hGzCKPGk0fpVHM0TD/FI1M 136 | Siwpn/CsmEleJd5oVg6KORr2ng2vsbR8wjNjNnnmNGdAkdDAjzuDElvKJzw7PMg70ZUBxRIaXFvC1vIJ 137 | e4TXkgwo1pABLeGS8gl7hed0BhQfIaP7JVxaPmHP8J7KgGILMvwu/OZ4efmEvcXPmQwongSKJ4HiSaB4 138 | EiieBIongeJJoHgOty+/ATfHCux1rAbuAAAAAElFTkSuQmCC 139 | 140 | 141 | 142 | 143 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 144 | YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8 145 | AAADs0lEQVR4XuWa3VHlMAyFKYESKIESqGCHEiiBEiiBBwqgAWYogRK2BEqgg3DOTO6O4xwp8l8Wxg/f 146 | A8expCSyLOdytSzL1EhxJqQ4E1KcCSnOhBRnQoozIcWZkOJMSHEmpDiCP28v1+AOPIFn8JHxCjh2D66V 147 | jRFIsRe8EfAI/oKlEM7h3BtluxdSbIVBA75RdWM10NaQByHFFhAo01jdRA+elM8WpFgLAnzPAh4Bl8at 148 | 8l+DFGtBYCxmKujefIF7FUMpUqwFQd0mQeZ8AlZ/Vvndeqa2jvEaXqts5DzkdkqRYgsIKq8BzIrit4U5 149 | 3DIjGdWUCVL0gEO+ZffJY5xVm2na/IZgg1lBW+rmCceqa4IUPeCMRYhOh+7PKfDFfsLrJThW1TxJ0QJO 150 | uD7/OVXXjII3CLwlUbVFSlEBB0zF3OmzunYU8HeUCcVZKcUcGgbWOuyyHUWBPz4EK5ZXNcdDijkw7KUe 151 | gznt8ELgT2XjhaIskGIKDEZa2w81dyT0mcVw4VFdbyHFCzDGvVg5UXTv0z3gz4qtqDhLkcCQt9Ys7pSt 152 | UcCf1TGGl4EUCYzUHGwY0Gn1AL7SbTklXJilCAP8EKEMR3hXNkcAX9YyCC/HnYDJ3oEmSlEhqgV+uEyV 153 | //BL2PyBiTQYPYkd0e3M7iH8kvCutPkDE3t+xqruz3Ngh1nJdM+x+gEWb46pOZsCuXFkwUnAakGHv2n4 154 | aKlJOZvs2DjywES5K+TXjQK+It8Gjth1rRsnHphodYRnrfWaviRn16ds/vDAZOsBNH/0iAJfXMMqhgjy 155 | 5LoTLGDAcl58AmsB/qzmx8Nsj6VoIQyTL3XtSODT+yaQw2VjLlMpWsCQ1R6ftgwI/HFbjNYDtymTogWM 156 | PWTGL3yq60cCn5Gt8bAjlKIFDHqV+JT2NwU+vQNb6GAmRQ8YtXYDd62NAP681j10NJeiBwx7WdCt/Y0C 157 | f2p3qj8NRoADb/39j4eQZmWfL0JHwJHXmvIhnPbDCVl9MjP7fhS1oKPVoXoAhGM9fhrjzsM37K5pjDOe 158 | Yn9SjAKHkf2YmVLz4yhvPC1w9NO9yEqxBAa1BpfetILX8HsDz+m7NKW2jrHVtex1ry9SLAVBlXRmrXQ9 159 | e0ixBgTG7bHHmf2IrlkgxRYQHLfIUdnAYvjzlkAOg1yD7fUgfs+/yaUgaD4IVvOapcFUZzYNbaqkOALe 160 | CGDbysxgpedDSeFb5hh3gtM6SSnOhBRnQoozIcWZkOJMSHEmpDgTUpwJKc6EFOdhufoGWATJBouDId0A 161 | AAAASUVORK5CYII= 162 | 163 | 164 | 165 | 166 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 167 | YQUAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8 168 | AAAEHklEQVR4XuXaW0tUXRzHcV/CvIN8Cd5H5UXdeDVI3iTRGCgUBCKdKALvIuiixK418kzZkIgGOY7j 169 | dAAzxjQL6SBSeNw6BE6Xu/8/ZslqzW/N3rNnzz6wLz7w8EOfvb6rPeXzUJ1pmpEGxyiBY5TAMUrgGCVw 170 | DJqDd29jJEGSJF3UTxrR11cCjkFCkXGSJ6YGX0YMfa8dcAwKCuNfdRStyhFHlwDHIDCyXR1KpBVHlwBH 171 | v+1lTiWIaWTvFJRIKxVfAhz9JOIFJ5ewNztzDP27ETj6RY0XKrkEI5vZ3J6aXN6anLD1JsDRD8d7XiR6 172 | hx4W0AUwO5dgZNIGhZtFOdKAniWDo9c4npjM6SUo8UKelL0EOHpJjnd6CZp4YZ1oPw5w9AqKF+xegkW8 173 | kETPZ3D0AkVq4wWrSzDmUvsgVgd+FEoGL1CcZbxwbeTZzursRXgJO6nrBRCq8wCdpWSoNYqyHX+6b8Zo 174 | Gpo3m4fTBRcuIY3OUzLUEkVVHC+4cAn+XgBFOY536RLgb4QlQy1QVNXxQhWXkEBnKxncRlGuxQsOLmEd 175 | nY3B0S0U5Xq8wH86oAtg4BK0/+cIjm6gqJrF89fz95X7OUG6BPjqC3CsFh2u5vFCuUvYTV3pQOeTwbEa 176 | dCjP4gXNJZT9lRfg6BQdxvN4QbkEW/EMjk7QIXyLF4qXYDuewbFS9HDf44sqimdwrAQ/VDmEVtDiGRzt 177 | 4ocqh9AKYjyDox38UOUQWkGNZ3C0wg9VDqEV5HgGx3LooXHlEFpBj2dwLOfX5/TK0kJq88fy7PraUnrr 178 | 3tj0IThcKOIZHHV+f8/UE1PV+/xlQT5gWOIZHHUotlONZwdf544uIEzxDI46FJtT44VbA1Ohi2dwRCgS 179 | vv5CGOMZHBGKhK8/W1vJmFeT89sc1jqa2VdjVUGJZ3BEKDSthut8XMqYzcOZP0GPZ3BUUVRMjSzn4Mu0 180 | sfj6aeDjGRxVFJVQI3U4fnfxiclaR+aOPg5BjGdwVFFYUg1F5HhGH4NCkOMZHGUUZuv1V+MZx595nNoF 181 | kTqexjM4yijO8vVH8ezTmzHz58Lgv3/eej9Q6Bkd/+8nRoXn8QyOMgos+/rr4nXuDibRfzv4Es/gKKPI 182 | vBotVBrPPqSHN4ISz+AoUGRcjRacxDPlAnyNZ3AUKLRfDWdO41n21ai4AN/jGRwFii15/auJZ6vzQymK 183 | t/zra16BI7t5ua3pQkvzt4m++4cuxOdIJ6lHz/ITHNmJk43dxGRd7ee3NxbG96UgO5IkQRz9LW6vwJFR 184 | eE5cALvUejYvxemEIloGRwqOyfHC+KPbh1Is40vpJ6GKlsGRYhNqPGtriW9I0XH0vWEDR4rtV+L549B9 185 | o/1cE/r6MIMjxTYQvgR+E0L5atsFxyiBY5TAMUrgGCVwjA6z7i9TW+fsWTsLrAAAAABJRU5ErkJggg== 186 | 187 | 188 | 189 | 190 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL 191 | EwAACxMBAJqcGAAAAr5JREFUeF7tmE9u00AUxg1iARKoFNupKlGxRaw5ACzY8ucGLDgAXSAh1fF0wQE4 192 | AgsOwBGQWHdfEEuWiBWL2qPGzHM+R6R9cfLGY1fjzE/6pM5L5sv7XpPOpFEgEAgEAoHAVZFO9TEJy+2i 193 | Dp/rqta2DWEp/LYNgQ3faOxDWArf8jOePi64kM161eOjYVW4poblOIfQFqqpY1kzqiGsC9M8huWC0Qxh 194 | EWRFiFUDINbt9YZY6Sf48RJtAyDa9o6CdQMYPWEAYxjAROmXTZBNha2XBvD/czYRvTa2Xg3xu9mdNC9/ 195 | cc21Cds7D4Bem3rA9uExR9THupFMf3+gqpsob0wTBMuNuX84uxXn+me93/SA8rDEqnycZsU5NRFP9VOU 196 | RdgOgJhM9bN6v+mBekF5IFR1w/zWT6iBJCs/oSqmywCINCs/z4egT6gnlPsnmZZv6/B58XtfVQnKYroO 197 | YE/NJmle/Kl7MT2h3C+7R7ODOC/+zidfvEbZiq4DIJKseEMe1BP1hnJ/JHn5BY1/jaLqGspWuBiAeetf 198 | N3+DvpEP9YZqPyzO/KwoYnX2EGVrnAzAcE+dPTIfhZK8ersbLJ35jr6huRoAYXr6MPfr6W7Q9czncDmA 199 | Xu8GLs58DpcDIPq5Gzg684fC+d3A1Zk/FE7vBi7P/CFxdjdweeYPiqu7AcJbCzad4bwlgo0czkwi2HSG 200 | 85YINnI4M4lg0xnOWyLYyOHMJIJNZzhviWAjhzMTSVW3YWXN/ArOeAsEKzmcmUSx0s9hZY3NP10vClZy 201 | ODOhTndUdRd2YnbeV7v03YPxFQl2cjgzC52aO/oLyceB3vZmzyuz98cFLyvBVg5n5qMQRw5n5qMQRw5n 202 | 5qMQRw5n5qMQRw5n5qMQRw5n5qMQRw5n5qMQRw5n5qMQRw5n5qMQRw5n5qMQJxAIBAKBQABE0T9ivyu/ 203 | 0WrxJgAAAABJRU5ErkJggg== 204 | 205 | 206 | 207 | 208 | iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL 209 | EwAACxMBAJqcGAAAAMNJREFUSEvtkUEKwjAQRbPwBk1S3Yl4AG+gggfxGoLNeDQP4hHEbUlHEpshBYW0 210 | TW27y4e3CZN5zS9LicryYkQGeOhCQLn148OSg5FC6ZdQaLvIFD4Y2IW/Fh/6usDCIFd99tfiM0TgXvFd 211 | Wxs/ddJBYNk4tJGApxkFNQXeSCBA7+qD++Q0/yuHcu1sU+OaIcHsFSVBK0nQSyPgRbUPDoyEqwpIsALL 212 | udLP0ND/6HdW4JEELiSpX+LqmgIJ5cavTukLYx9s83oysYQORQAAAABJRU5ErkJggg== 213 | 214 | 215 | 216 | 217 | iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAL 218 | EwAACxMBAJqcGAAAAN1JREFUSEvtkUEOATEUhmtjKzKthTVLiQtI3ARnsGDRaR1F4gguILN3AWewp62o 219 | 1/FY1evMRkLmS/6Mxfu/f2TYf9KTZiRysxfKHT5GmzHP7QQr9UC5p5JpN+XKnLm8DrFWHRCEt4yKXwkD 220 | +PvUX98zrFYDSnUGQgqmfRvraaBQd8BzZXdYTwOF5AB8YCWUWYjcbd7RlwEqaECQHCgj3RGe4fYZaeao 221 | oMFCXEoF/gUqaOC4GaCB42aABo6bARo4/vEBLu02KkjmNkMFTXflO5myy/BGlVPKfQsV34KxBxA04qwG 222 | 1E42AAAAAElFTkSuQmCC 223 | 224 | 225 | -------------------------------------------------------------------------------- /Explorer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Explorer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string vfsfile { 30 | get { 31 | return ((string)(this["vfsfile"])); 32 | } 33 | set { 34 | this["vfsfile"] = value; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Explorer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Explorer/Res/698926-icon-93-inbox-upload-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/698926-icon-93-inbox-upload-64.png -------------------------------------------------------------------------------- /Explorer/Res/698928-icon-95-folder-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/698928-icon-95-folder-24.png -------------------------------------------------------------------------------- /Explorer/Res/699326-icon-54-document-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/699326-icon-54-document-24.png -------------------------------------------------------------------------------- /Explorer/Res/delete-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/delete-01.png -------------------------------------------------------------------------------- /Explorer/Res/refresh-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/refresh-01.png -------------------------------------------------------------------------------- /Explorer/Res/rename-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/Explorer/Res/rename-01.png -------------------------------------------------------------------------------- /Explorer/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace Explorer.Properties { 2 | 3 | 4 | // 通过此类可以处理设置类的特定事件: 5 | // 在更改某个设置的值之前将引发 SettingChanging 事件。 6 | // 在更改某个设置的值之后将引发 PropertyChanged 事件。 7 | // 在加载设置值之后将引发 SettingsLoaded 事件。 8 | // 在保存设置值之前将引发 SettingsSaving 事件。 9 | internal sealed partial class Settings { 10 | 11 | public Settings() { 12 | // // 若要为保存和更改设置添加事件处理程序,请取消注释下列行: 13 | // 14 | // this.SettingChanging += this.SettingChangingEventHandler; 15 | // 16 | // this.SettingsSaving += this.SettingsSavingEventHandler; 17 | // 18 | } 19 | 20 | private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { 21 | // 在此处添加用于处理 SettingChangingEvent 事件的代码。 22 | } 23 | 24 | private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { 25 | // 在此处添加用于处理 SettingsSaving 事件的代码。 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Explorer/ShellFileInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Runtime.InteropServices; 7 | using System.Drawing; 8 | 9 | namespace Explorer 10 | { 11 | public class ShellFileInfo 12 | { 13 | /// 14 | /// Options to specify the size of icons to return. 15 | /// 16 | public enum IconSize 17 | { 18 | /// 19 | /// Specify large icon - 32 pixels by 32 pixels. 20 | /// 21 | Large = 0, 22 | /// 23 | /// Specify small icon - 16 pixels by 16 pixels. 24 | /// 25 | Small = 1 26 | } 27 | 28 | /// 29 | /// Options to specify whether folders should be in the open or closed state. 30 | /// 31 | public enum FolderType 32 | { 33 | /// 34 | /// Specify open folder. 35 | /// 36 | Open = 0, 37 | /// 38 | /// Specify closed folder. 39 | /// 40 | Closed = 1 41 | } 42 | 43 | public static string GetFileTypeDescription(string fileNameOrExtension) 44 | { 45 | SHFILEINFO shfi = new SHFILEINFO(); 46 | if (IntPtr.Zero != SHGetFileInfo( 47 | fileNameOrExtension, 48 | FILE_ATTRIBUTE_NORMAL, 49 | ref shfi, 50 | (uint)Marshal.SizeOf(typeof(SHFILEINFO)), 51 | SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME)) 52 | { 53 | return shfi.szTypeName; 54 | } 55 | return null; 56 | } 57 | 58 | public static Bitmap GetFileIcon(string name, IconSize size, 59 | bool linkOverlay) 60 | { 61 | SHFILEINFO shfi = new SHFILEINFO(); 62 | uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES; 63 | 64 | if (true == linkOverlay) flags += SHGFI_LINKOVERLAY; 65 | 66 | /* Check the size specified for return. */ 67 | if (IconSize.Small == size) 68 | { 69 | flags += SHGFI_SMALLICON; // include the small icon flag 70 | } 71 | else 72 | { 73 | flags += SHGFI_LARGEICON; // include the large icon flag 74 | } 75 | 76 | SHGetFileInfo(name, 77 | FILE_ATTRIBUTE_NORMAL, 78 | ref shfi, 79 | (uint)Marshal.SizeOf(shfi), 80 | flags); 81 | 82 | // Copy (clone) the returned icon to a new object, thus allowing us 83 | // to call DestroyIcon immediately 84 | Bitmap bitmap = (Bitmap)Bitmap.FromHicon(shfi.hIcon).Clone(); 85 | DestroyIcon(shfi.hIcon); // Cleanup 86 | return bitmap; 87 | } 88 | 89 | /// 90 | /// Used to access system folder icons. 91 | /// 92 | /// Specify large or small icons. 93 | /// Specify open or closed FolderType. 94 | /// System.Drawing.Icon 95 | public static Bitmap GetFolderIcon(IconSize size, FolderType folderType) 96 | { 97 | // Need to add size check, although errors generated at present! 98 | uint flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES; 99 | 100 | if (FolderType.Open == folderType) 101 | { 102 | flags += SHGFI_OPENICON; 103 | } 104 | 105 | if (IconSize.Small == size) 106 | { 107 | flags += SHGFI_SMALLICON; 108 | } 109 | else 110 | { 111 | flags += SHGFI_LARGEICON; 112 | } 113 | 114 | // Get the folder icon 115 | SHFILEINFO shfi = new SHFILEINFO(); 116 | SHGetFileInfo(null, 117 | FILE_ATTRIBUTE_DIRECTORY, 118 | ref shfi, 119 | (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi), 120 | flags); 121 | 122 | 123 | Bitmap bitmap = (Bitmap)Bitmap.FromHicon(shfi.hIcon).Clone(); 124 | 125 | DestroyIcon(shfi.hIcon); // Cleanup 126 | return bitmap; 127 | } 128 | 129 | /// 130 | /// Provides access to function required to delete handle. This method is used internally 131 | /// and is not required to be called separately. 132 | /// 133 | /// Pointer to icon handle. 134 | /// N/A 135 | [DllImport("User32.dll")] 136 | public static extern int DestroyIcon(IntPtr hIcon); 137 | 138 | [DllImport("shell32")] 139 | private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint flags); 140 | 141 | [StructLayout(LayoutKind.Sequential)] 142 | private struct SHFILEINFO 143 | { 144 | public IntPtr hIcon; 145 | public int iIcon; 146 | public uint dwAttributes; 147 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 148 | public string szDisplayName; 149 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] 150 | public string szTypeName; 151 | } 152 | 153 | private const uint FILE_ATTRIBUTE_READONLY = 0x00000001; 154 | private const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002; 155 | private const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004; 156 | private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; 157 | private const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020; 158 | private const uint FILE_ATTRIBUTE_DEVICE = 0x00000040; 159 | private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; 160 | private const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100; 161 | private const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200; 162 | private const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400; 163 | private const uint FILE_ATTRIBUTE_COMPRESSED = 0x00000800; 164 | private const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000; 165 | private const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000; 166 | private const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; 167 | private const uint FILE_ATTRIBUTE_VIRTUAL = 0x00010000; 168 | 169 | private const uint SHGFI_ICON = 0x000000100; // get icon 170 | private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name 171 | private const uint SHGFI_TYPENAME = 0x000000400; // get type name 172 | private const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes 173 | private const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location 174 | private const uint SHGFI_EXETYPE = 0x000002000; // return exe type 175 | private const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index 176 | private const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon 177 | private const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state 178 | private const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes 179 | private const uint SHGFI_LARGEICON = 0x000000000; // get large icon 180 | private const uint SHGFI_SMALLICON = 0x000000001; // get small icon 181 | private const uint SHGFI_OPENICON = 0x000000002; // get open icon 182 | private const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon 183 | private const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl 184 | private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Explorer/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Threading; 7 | 8 | namespace Explorer 9 | { 10 | class Utils 11 | { 12 | public static String FormatSize(long size) 13 | { 14 | string[] sizes = { "B", "KB", "MB", "GB" }; 15 | double len = size; 16 | int order = 0; 17 | while (len >= 1024 && order + 1 < sizes.Length) 18 | { 19 | order++; 20 | len = len / 1024; 21 | } 22 | 23 | return String.Format("{0:0.##} {1}", len, sizes[order]); 24 | } 25 | 26 | public static void ProcessUITasks() 27 | { 28 | DispatcherFrame frame = new DispatcherFrame(); 29 | Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate (object parameter) { 30 | frame.Continue = false; 31 | return null; 32 | }), null); 33 | Dispatcher.PushFrame(frame); 34 | } 35 | 36 | public static byte[] GetBytes(string str) 37 | { 38 | byte[] bytes = new byte[str.Length * sizeof(char)]; 39 | System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length); 40 | return bytes; 41 | } 42 | 43 | public static string GetString(byte[] bytes) 44 | { 45 | char[] chars = new char[bytes.Length / sizeof(char)]; 46 | System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length); 47 | return new string(chars); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VFS 2 | 3 | ## 综述 4 | 5 | 本项目使用 C# 实现了一个虚拟文件系统,并基于该虚拟文件系统接口,使用 WPF 技术开发了操作界面。 6 | 7 | ### 文件系统功能特性 8 | - 索引文件系统,可在格式化时调整索引节点数量和区块大小 9 | - 使用位图进行空间管理和索引节点管理(一次性检查 32 位) 10 | - 真实的空间管理,虚拟文件的写入和读取操作可对应到实际虚拟磁盘文件物理地址 11 | - 使用红黑树进行目录项管理(Dictionary 集合) 12 | - 支持任意长度文件名和任意深度路径 13 | - 数据区块支持二级查表,单个文件最大可达 2GB(Int32 限制) 14 | - 最大支持总存储空间 1.5GB(内存映射文件限制) 15 | - 支持文件随机访问和读取,也支持顺序访问和读取 16 | - 支持 Seek,可以快速创建很大的空文件 17 | - 懒惰分配数据区块,即只有在需要对数据区块写入时,才会查找并分配区块 18 | - 底层使用内存映射文件存储,接口上支持纯内存存储(并没有实现) 19 | - 所有写入操作都会立即持久化 20 | - 位图存储在内存中,不需要从虚拟磁盘文件上一遍遍读取 21 | - 支持硬链接 22 | - 面向对象的设计 23 | - 提供了与操作系统文件操作 API 极其相似的应用程序接口 24 | - API 与底层实现分离,调用者不需要了解底层相关信息(如数据区块等) 25 | 26 | ### 操作界面功能特性 27 | - 基于 WPF,界面美观,接近 Windows 10 资源管理器 28 | - 支持从本地磁盘添加文件 29 | - 支持使用操作系统程序打开读取虚拟文件系统中的文件 30 | - 支持使用操作系统程序编辑文件并自动写回虚拟文件系统 31 | 32 | ### 由于时间关系,该项目还有很多可以改进的地方 33 | 34 | - 没有为内存映射文件存储编写内存缓存,因此其执行效率远远比不上纯内存存储 35 | - 没有为顺序写入或顺序读取优化,而是直接将这些操作转化为了对各块的随机读取和写入,因此顺序写入和读取的效率很低 36 | - 没有分区域按需进行内存映射,而是直接映射了整个虚拟磁盘文件,因此运行时可能会占用较多共享内存空间,甚至可能由于内存不足而无法启动 37 | - 目录项结构没有使用 B 树或 B+ 树存储,且由于目录结构建立在序列化之上,因此每次修改目录都需要重写所有其占用的区块 38 | - 目前查询空闲空间时,当检查到 32 位区块有空闲空间后,最多还需要 31 次比较操作才能获得空闲编号,该操作可进一步利用二分查找优化 39 | - 空闲空间管理可以由位图优化为更高级的数据结构,来加速查询 40 | - 没有编写复制、粘贴、创建链接的图形化界面 41 | 42 | ## 代码说明 43 | bin/Explorer.exe 是该项目虚拟文件系统操作界面(文件管理器)。您至少需要 .net Framework 4 才可以运行它。它目标平台为 x86,因此 x86 和 x64 操作系统上都可以运行。推荐在 Windows 7 及以上操作系统中使用。 44 | 45 | src/ 下是 VS 2015 解决方案。该项目由 C# 写成。由于文件管理器部分界面使用了第三方库,因此您可能并不能直接编译该项目。请参阅本文末查看解决方案。 46 | 47 | ## 具体实现 48 | 49 | ### 1. VFS 50 | 51 | VFS(Virtual File System) 实现了一套界面无关的虚拟文件系统接口。主要由以下几层组成: 52 | 53 | #### 1.1 DeviceAdapter/AbstractDevice 54 | 55 | 存储介质,即该文件系统建立在什么样的存储介质之上。AbstractDevice 是一个抽象类,需要实现如下接口: 56 | 57 | `Size()`:获取该存储介质总共的可用空间大小 58 | `Read(long position)`:读取该介质 position 处,长度为 sizeof(T) 的数据到值类型 T 并返回 59 | `ReadArray(long position, int count)`:读取该介质 position 处,长度为 sizeof(T)*count 的数据 60 | `Write(long position, T structure)`:将值类型 T 写入该介质 position 处,共写入 sizeof(T) 字节 61 | `WriteArray(long position, T[] array, int offset, int count)`:写入 T[],不在赘述 62 | 63 | 基于 AbstractDevice,内置实现了 FileAdapter,即物理磁盘上的虚拟磁盘文件作为 VFS 存储介质。 64 | 65 | #### 1.2 VFSCore 66 | 67 | 虚拟文件系统对象。它需要建立在一个存储介质之上,提供了综合性函数,如格式化磁盘等。它对 inode 和数据区块位图进行缓存,包含以下成员: 68 | 69 | ```c# 70 | /// 71 | /// 存储介质对象 72 | /// 73 | private AbstractDevice device; 74 | 75 | /// 76 | /// 超级块对象 77 | /// 78 | private SuperBlock superBlock; 79 | 80 | /// 81 | /// inode 位图 82 | /// 83 | private UInt32[] inodeBitmaps; 84 | 85 | /// 86 | /// 数据块位图 87 | /// 88 | private UInt32[] blockBitmaps; 89 | ``` 90 | 91 | 存储介质上空间排布由低到高如下: 92 | 93 | `_SuperBlock | inodeBitmapVector[] | _INode[] | blockBitmapVector[] | Block[]` 94 | 95 | 注,各个结构之间并没有按照指定大小对齐,它们都是紧密排列的。如,`_SuperBlock` 地址位于 `0`,则 `inodeBitmapVector[]` 位于 `0+sizeof(_SuperBlock)`,`_Inode[]` 位于 `0+sizeof(_SuperBlock)+4*count(inodeBitmapVector[]),blockBitmapVector[]` 位于`0+sizeof(_SuperBlock)+4*count(inodeBitmapVector[])+sizeof(_INode)*count(_Inode[])` 等等。另外,`Block[]` 之后还会有一小部分没有被使用的空间(由分配算法产生)。 96 | 97 | #### 1.3 SuperBlock 98 | 99 | 超级块操作对象,存储了该文件系统格式化后的参数。`SuperBlock` 包含 `_SuperBlock` 结构体,`_SuperBlock` 结构体包含了这些关键数据,并且会被写入到存储介质中。`_SuperBlock` 结构体总是会被写入到存储介质中的 `0` 号位置。 100 | 101 | `_SuperBlock` 包含: 102 | 103 | ```c# 104 | /// 105 | /// 可以容纳多少个 inode 106 | /// 107 | public UInt32 inodeCapacity; 108 | 109 | /// 110 | /// 已经分配了多少个 inode 111 | /// 112 | public UInt32 inodeAllocated; 113 | 114 | /// 115 | /// 每个数据块大小 116 | /// 117 | public UInt16 blockSize; 118 | 119 | /// 120 | /// 可以容纳多少个数据块 121 | /// 122 | public UInt32 blockCapacity; 123 | 124 | /// 125 | /// 已经预留了多少个数据块 126 | /// 127 | public UInt32 blockPreserved; 128 | 129 | /// 130 | /// 已经实际分配了多少个数据块 131 | /// 132 | public UInt32 blockAllocated; 133 | 134 | /// 135 | /// 有效标识符,必须为 0x1234 136 | /// 标识符放在最后,这样 SuperBlock 之前字段数量变更后就会要求重新格式化,避免潜在数据问题 137 | /// 138 | public UInt16 magicValue; 139 | ``` 140 | 141 | `_SuperBlock` 最后一个字段是 `magicValue`,其在存储介质上的位置是 `4+4+2+4+4+4` 字节处。VFS 使用该数据判断这个文件系统是否有效,如果无效,则会要求格式化。(创建空磁盘介质时,该位置数据是 `0`,因此会要求格式化) 142 | 143 | `SuperBlock` 是对 `_SuperBlock` 结构体的封装,提供了一些便捷的预计算值(显然,这些数据可以由超级块结构体所包含的数据计算出来),方便上层代码计算: 144 | 145 | ```c# 146 | /// 147 | /// 可持久化数据 148 | /// 149 | public _SuperBlock data; 150 | 151 | /// 152 | /// inode 位图起始地址 153 | /// 154 | public UInt32 pInodeBitVectors; 155 | 156 | /// 157 | /// inode 区起始地址 158 | /// 159 | public UInt32 pInodeData; 160 | 161 | /// 162 | /// 数据块位图起始地址 163 | /// 164 | public UInt32 pBlockBitVectors; 165 | 166 | /// 167 | /// 数据块区起始地址 168 | /// 169 | public UInt32 pBlockData; 170 | 171 | /// 172 | /// 存储介质 173 | /// 174 | private VFSCore vfs; 175 | ``` 176 | 177 | #### 1.4 Block 178 | 179 | 数据区块操作对象,构造时需要提供“存储介质”对象和所要操作的数据区块编号。一个数据区块可容纳的数据量在格式化时确定。该对象并不存储区块中的具体内容,相反,它提供了读取和访问的封装函数,对存储介质进行读取和访问。 180 | 181 | 事实上,该对象提供的是对某个特定编号区块特定位置进行读写操作的接口。这样,上层代码无需关心需要向数据区块写入数据在存储介质上的具体位置。这些计算操作由区块操作对象完成。 182 | 183 | #### 1.5 INode 184 | 185 | `INode` 操作对象。`INode` 包含 `_INode` 结构体,`_INode` 结构体会被持久化到磁盘介质中。`INode` 操作对象可以从存储介质上创建(载入),也可以新分配。 186 | 187 | `INode` 操作对象包含一个 `WeakValueDictionary` 弱引用集合静态成员。当 `INode` 操作对象从存储介质中载入时,对于相同的 inode 编号将会获取到相同的 `INode` 操作对象实例,这样,对 `_INode` 结构体的修改不会导致不一致。弱引用集合使得这些操作对象的内存可以被操作系统回收,但又可以保持同样编号总是引用到相同的实例。 188 | 189 | `_INode` 结构体包含: 190 | 191 | ```c# 192 | /// 193 | /// 文件大小,字节为单位 194 | /// 195 | public UInt32 sizeByte; 196 | 197 | /// 198 | /// 最后访问时间戳 199 | /// 200 | public UInt64 accessTime; 201 | 202 | /// 203 | /// 最后修改时间戳 204 | /// 205 | public UInt64 modifyTime; 206 | 207 | /// 208 | /// 创建时间戳 209 | /// 210 | public UInt64 creationTime; 211 | 212 | /// 213 | /// 链接数 214 | /// 215 | public UInt32 linkCount; 216 | 217 | /// 218 | /// 属性,最低位0代表是文件,最低位1代表是目录 219 | /// 220 | public UInt32 flags; 221 | 222 | /// 223 | /// 所有者 ID 224 | /// 225 | public UInt32 owner; 226 | 227 | /// 228 | /// 数据块索引,前 12 块为直接索引,第 13 块为一次间接索引,第 14 块为二次间接索引 229 | /// 230 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] 231 | public UInt32[] dataBlockId; 232 | 233 | /// 234 | /// 当前 inode 已预留多少个数据块 235 | /// 236 | public UInt32 blockPreserved; 237 | ``` 238 | 239 | `INode` 包含: 240 | 241 | ```c# 242 | /// 243 | /// 可持久化数据 244 | /// 245 | public _INode data; 246 | 247 | /// 248 | /// 该 inode 编号 249 | /// 250 | public UInt32 index; 251 | 252 | /// 253 | /// 存储介质 254 | /// 255 | private VFSCore vfs; 256 | 257 | /// 258 | /// 使用直接索引的最大字节数 259 | /// 260 | private UInt32 BoundLv0; 261 | 262 | /// 263 | /// 使用一级间接索引的最大字节数 264 | /// 265 | private UInt32 BoundLv1; 266 | ``` 267 | 268 | #### 1.6 INodeDirectory 269 | 270 | `INodeDirectory` 是对目录项操作包装类。inode 要么是一个文件,要么是一个目录。当 inode 是一个目录时,可以使用 `INodeDirectory` 操作这个 inode 的内容(数据区块将被解读为目录结构)。 271 | 272 | `INodeDirectory` 使用 `Dictionary` 存储目录项,使用 protobuf 在 inode 上层进行压缩存储的的序列化与反序列化。 273 | 274 | #### 1.7 VFS 275 | 276 | VFS 是面向应用程序的文件系统操作类(VFS API),提供诸如格式化、创建文件操作类(`VFS.File`)、创建目录操作类(`VFS.Directory`)等接口。构造 VFS 时,调用者需要传入一个磁盘介质(`AbstractDevice`)。 277 | 278 | #### 1.8 VFS.File 279 | 280 | `VFS.File` 是面向应用程序的文件操作类(File API),提供诸如根据路径打开文件、移动文件指针、向指针处写入数据、从指针处读取数据等接口。 281 | 282 | #### 1.9 VFS.Directory 283 | 284 | `VFS.File` 是面向应用程序的目录操作类(Directory API),提供诸如创建新文件夹、查询文件夹内容、重命名或删除一个目录项等功能。 285 | 286 | ### 2. Explorer 287 | 288 | 文件浏览器部分使用 WPF 技术实现,使用了 infragistics WPF 库帮助进行界面绘制。也正是由于这个原因,如果您需要自行编译文件浏览器部分代码,请先在本机安装 infragistics WPF 库。 289 | 290 | infragistics 是商业软件。您可以从该地址获得一份非法拷贝:http://www.cnblogs.com/zeroone/p/4540900.html 291 | -------------------------------------------------------------------------------- /VirtualFileSystem.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualFileSystem", "VirtualFileSystem\VirtualFileSystem.csproj", "{362E0E54-3B00-4C19-8F58-90F8E5430F95}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Explorer", "Explorer\Explorer.csproj", "{04CADF34-E149-4041-A344-75046CBF870A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Debug|x86.ActiveCfg = Debug|x86 21 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Debug|x86.Build.0 = Debug|x86 22 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Release|x86.ActiveCfg = Release|x86 25 | {362E0E54-3B00-4C19-8F58-90F8E5430F95}.Release|x86.Build.0 = Release|x86 26 | {04CADF34-E149-4041-A344-75046CBF870A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {04CADF34-E149-4041-A344-75046CBF870A}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {04CADF34-E149-4041-A344-75046CBF870A}.Debug|x86.ActiveCfg = Debug|x86 29 | {04CADF34-E149-4041-A344-75046CBF870A}.Debug|x86.Build.0 = Debug|x86 30 | {04CADF34-E149-4041-A344-75046CBF870A}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {04CADF34-E149-4041-A344-75046CBF870A}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {04CADF34-E149-4041-A344-75046CBF870A}.Release|x86.ActiveCfg = Release|Any CPU 33 | {04CADF34-E149-4041-A344-75046CBF870A}.Release|x86.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /VirtualFileSystem/Block.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VirtualFileSystem 4 | { 5 | public class Block 6 | { 7 | /// 8 | /// 该数据块编号 9 | /// 10 | public UInt32 index; 11 | 12 | /// 13 | /// 存储介质 14 | /// 15 | private VFSCore vfs; 16 | 17 | public Block(VFSCore vfs, UInt32 index = UInt32.MaxValue) 18 | { 19 | this.vfs = vfs; 20 | this.index = index; 21 | } 22 | 23 | public UInt32 GetPosition(UInt32 offset) 24 | { 25 | return vfs.GetSuperBlock().pBlockData + index * vfs.GetSuperBlock().data.blockSize + offset; 26 | } 27 | 28 | /// 29 | /// 读取数据 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | public T Read(UInt32 offset) where T : struct 36 | { 37 | UInt32 position = GetPosition(offset); 38 | return vfs.GetDevice().Read(position); 39 | } 40 | 41 | /// 42 | /// 读取数据 43 | /// 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | public T[] ReadArray(UInt32 offset, int count) where T : struct 50 | { 51 | UInt32 position = GetPosition(offset); 52 | return vfs.GetDevice().ReadArray(position, count); 53 | } 54 | 55 | /// 56 | /// 读取数据 57 | /// 58 | /// 59 | /// 60 | /// 61 | /// 62 | /// 63 | /// 64 | public void ReadArray(UInt32 offset, T[] array, int arrOffset, int count) where T : struct 65 | { 66 | UInt32 position = GetPosition(offset); 67 | vfs.GetDevice().ReadArray(position, array, arrOffset, count); 68 | } 69 | 70 | /// 71 | /// 写入数据 72 | /// 73 | /// 74 | /// 75 | /// 76 | /// 77 | public void Write(UInt32 offset, T structure) where T : struct 78 | { 79 | UInt32 position = GetPosition(offset); 80 | vfs.GetDevice().Write(position, structure); 81 | } 82 | 83 | /// 84 | /// 写入数据 85 | /// 86 | /// 87 | /// 88 | /// 89 | /// 90 | /// 91 | /// 92 | public void WriteArray(UInt32 offset, T[] array, int arrOffset, int count) where T : struct 93 | { 94 | UInt32 position = GetPosition(offset); 95 | vfs.GetDevice().WriteArray(position, array, arrOffset, count); 96 | } 97 | 98 | /// 99 | /// 创建一个新的数据块 100 | /// 101 | /// 102 | /// 103 | /// 104 | public static Block Create(VFSCore vfs, UInt32 index, Byte fill = 0) 105 | { 106 | if (index >= vfs.GetSuperBlock().data.blockCapacity) 107 | { 108 | throw new Exception("无效 block 编号"); 109 | } 110 | Block block = new Block(vfs, index); 111 | 112 | Byte[] data = new Byte[vfs.GetSuperBlock().data.blockSize]; 113 | for (var i = 0; i < data.Length; ++i) 114 | { 115 | data[i] = fill; 116 | } 117 | block.WriteArray(0, data, 0, data.Length); 118 | 119 | return block; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /VirtualFileSystem/DeviceAdapter/AbstractDevice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VirtualFileSystem.DeviceAdapter 4 | { 5 | public abstract class AbstractDevice 6 | { 7 | public abstract UInt32 Size(); 8 | 9 | public abstract T Read(long position) where T : struct; 10 | 11 | public abstract T[] ReadArray(long position, int count) where T : struct; 12 | 13 | public abstract void ReadArray(long position, T[] array, int offset, int count) where T : struct; 14 | 15 | public abstract void Write(long position, T structure) where T : struct; 16 | 17 | public abstract void WriteArray(long position, T[] array, int offset, int count) where T : struct; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /VirtualFileSystem/DeviceAdapter/FileAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.MemoryMappedFiles; 4 | 5 | namespace VirtualFileSystem.DeviceAdapter 6 | { 7 | // Use physics file as the underlayer storage 8 | public class FileAdapter : AbstractDevice 9 | { 10 | private UInt32 sizeByte; 11 | private MemoryMappedFile mmf = null; 12 | 13 | private MemoryMappedViewAccessor accessor = null; 14 | 15 | /// 16 | /// 物理文件适配器,若不存在文件,则会创建一个新文件并打开,若已存在,则会打开文件。 17 | /// 使用 Memory Mapping 技术提高随机访问性能。 18 | /// 19 | /// 存储文件名 20 | /// 预分配文件大小 21 | public FileAdapter(String path, UInt32 sizeByte) 22 | { 23 | this.sizeByte = sizeByte; 24 | 25 | if (!File.Exists(path)) 26 | { 27 | var file = File.Create(path); 28 | file.SetLength(sizeByte); 29 | file.Close(); 30 | } 31 | 32 | mmf = MemoryMappedFile.CreateFromFile(path, FileMode.Open); 33 | accessor = mmf.CreateViewAccessor(); 34 | } 35 | 36 | public override UInt32 Size() 37 | { 38 | return sizeByte; 39 | } 40 | 41 | public override T Read(long position) 42 | { 43 | int size = Utils.GetStructSize(); 44 | byte[] data = new byte[size]; 45 | accessor.ReadArray(position, data, 0, size); 46 | 47 | return Utils.ByteToStruct(data); 48 | } 49 | 50 | public override T[] ReadArray(long position, int count) 51 | { 52 | var tt = new T[count]; 53 | accessor.ReadArray(position, tt, 0, count); 54 | return tt; 55 | } 56 | 57 | public override void ReadArray(long position, T[] array, int offset, int count) 58 | { 59 | accessor.ReadArray(position, array, offset, count); 60 | } 61 | 62 | public override void Write(long position, T structure) 63 | { 64 | byte[] arr = Utils.StructToByte(structure); 65 | accessor.WriteArray(position, arr, 0, arr.Length); 66 | } 67 | 68 | public override void WriteArray(long position, T[] array, int offset, int count) 69 | { 70 | accessor.WriteArray(position, array, offset, count); 71 | } 72 | 73 | ~FileAdapter() 74 | { 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /VirtualFileSystem/INodeDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using ProtoBuf; 6 | 7 | namespace VirtualFileSystem 8 | { 9 | class INodeDirectory 10 | { 11 | private VFSCore vfs; 12 | 13 | /// 14 | /// 目录关联的 inode 节点 15 | /// 16 | private INode inode; 17 | 18 | /// 19 | /// 目录项 20 | /// 21 | private Dictionary entries; 22 | 23 | public INodeDirectory(VFSCore vfs, INode inode) 24 | { 25 | this.vfs = vfs; 26 | this.inode = inode; 27 | this.entries = new Dictionary(); 28 | } 29 | 30 | /// 31 | /// 包含目录项个数 32 | /// 33 | /// 34 | public int Size() 35 | { 36 | return entries.Count; 37 | } 38 | 39 | /// 40 | /// 返回是否包含目录项 41 | /// 42 | /// 43 | /// 44 | public Boolean Contains(String name) 45 | { 46 | return entries.ContainsKey(name); 47 | } 48 | 49 | /// 50 | /// 查找目录项的 inode 51 | /// 52 | /// 53 | /// 54 | public UInt32 Find(String name) 55 | { 56 | return entries[name]; 57 | } 58 | 59 | /// 60 | /// 持久化该目录 61 | /// 62 | private void Save() 63 | { 64 | MemoryStream ms = new MemoryStream(); 65 | Serializer.Serialize(ms, entries); 66 | byte[] bytes = ms.ToArray(); 67 | inode.Write(bytes); 68 | } 69 | 70 | /// 71 | /// 从存储介质上载入该目录 72 | /// 73 | public void Load() 74 | { 75 | byte[] bytes = inode.Read(); 76 | MemoryStream ms = new MemoryStream(bytes); 77 | entries = Serializer.Deserialize>(ms); 78 | } 79 | 80 | /// 81 | /// 添加本目录 82 | /// 83 | /// 84 | /// 85 | private Boolean AddSelf() 86 | { 87 | if (Contains(".")) 88 | { 89 | return false; 90 | } 91 | 92 | entries["."] = inode.index; 93 | Save(); 94 | 95 | return true; 96 | } 97 | 98 | /// 99 | /// 添加父目录 100 | /// 101 | /// 102 | /// 103 | private Boolean AddParent(UInt32 inodeIndex) 104 | { 105 | if (Contains("..")) 106 | { 107 | return false; 108 | } 109 | 110 | entries[".."] = inodeIndex; 111 | Save(); 112 | 113 | return true; 114 | } 115 | 116 | /// 117 | /// 添加一个文件目录项 118 | /// 119 | /// 120 | /// 121 | /// 122 | public Boolean Add(String name, INode inode) 123 | { 124 | if (Contains(name)) 125 | { 126 | return false; 127 | } 128 | 129 | entries[name] = inode.index; 130 | inode.data.linkCount++; 131 | inode.Save(); 132 | 133 | Save(); 134 | return true; 135 | } 136 | 137 | /// 138 | /// 添加一个目录目录项 139 | /// 140 | /// 141 | /// 142 | /// 143 | public Boolean Add(String name, INodeDirectory dir) 144 | { 145 | if (Contains(name)) 146 | { 147 | return false; 148 | } 149 | if (dir.Contains("..")) 150 | { 151 | return false; 152 | } 153 | 154 | entries[name] = dir.inode.index; 155 | dir.inode.data.linkCount++; 156 | dir.inode.Save(); 157 | dir.AddParent(inode.index); 158 | 159 | Save(); 160 | return true; 161 | } 162 | 163 | /// 164 | /// 重命名一个目录项 165 | /// 166 | /// 167 | /// 168 | /// 169 | public Boolean Rename(String oldName, String newName) 170 | { 171 | if (!Contains(oldName)) 172 | { 173 | return false; 174 | } 175 | if (oldName == "." || oldName == "..") 176 | { 177 | return false; 178 | } 179 | if (Contains(newName)) 180 | { 181 | return false; 182 | } 183 | var inodeIndex = entries[oldName]; 184 | entries.Remove(oldName); 185 | entries[newName] = inodeIndex; 186 | Save(); 187 | return true; 188 | } 189 | 190 | /// 191 | /// 删除一个目录项 192 | /// 193 | /// 194 | /// 195 | public Boolean Delete(String name) 196 | { 197 | if (!Contains(name)) 198 | { 199 | return false; 200 | } 201 | if (name == "." || name == "..") 202 | { 203 | return false; 204 | } 205 | 206 | var inodeIndex = entries[name]; 207 | 208 | INode inode = INode.Load(vfs, inodeIndex); 209 | if (inode.IsDirectory()) 210 | { 211 | // 删除非空目录项:递归删除 212 | INodeDirectory id = INodeDirectory.Load(vfs, inodeIndex); 213 | if (id.Size() > 2) 214 | { 215 | var l = id.List(); 216 | foreach (var pair in l) 217 | { 218 | id.Delete(pair.Key); 219 | } 220 | } 221 | } 222 | 223 | inode.data.linkCount--; 224 | 225 | if (inode.data.linkCount == 0) 226 | { 227 | inode.Resize(0); 228 | vfs.DeAllocateINode(inode.index); 229 | } 230 | else 231 | { 232 | inode.Save(); 233 | } 234 | 235 | entries.Remove(name); 236 | Save(); 237 | 238 | return true; 239 | } 240 | 241 | /// 242 | /// 列出所有目录项 243 | /// 244 | /// 245 | public List> List() 246 | { 247 | return entries.ToList(); 248 | } 249 | 250 | /// 251 | /// 创建一个新目录 252 | /// 253 | /// 254 | /// 255 | public static INodeDirectory Create(VFSCore vfs) 256 | { 257 | INode inode = vfs.AllocateINode(1, 2333); 258 | INodeDirectory t = new INodeDirectory(vfs, inode); 259 | t.AddSelf(); 260 | return t; 261 | } 262 | 263 | /// 264 | /// 根据 inode index 建立 INodeDirectory 265 | /// 266 | /// 267 | /// 268 | /// 269 | public static INodeDirectory Load(VFSCore vfs, UInt32 inodeIndex) 270 | { 271 | INode inode = INode.Load(vfs, inodeIndex); 272 | return Load(vfs, inode); 273 | } 274 | 275 | /// 276 | /// 根据 inode 建立 INodeDirectory 277 | /// 278 | /// 279 | /// 280 | /// 281 | public static INodeDirectory Load(VFSCore vfs, INode inode) 282 | { 283 | INodeDirectory t = new INodeDirectory(vfs, inode); 284 | t.Load(); 285 | return t; 286 | } 287 | 288 | /// 289 | /// 根据路径解析目录,路径必须以 / 结尾 290 | /// 291 | /// 292 | /// 293 | public static INodeDirectory Resolve(VFSCore vfs, String path) 294 | { 295 | INodeDirectory root = Load(vfs, 0); 296 | 297 | var pathCom = path.Split('/'); 298 | var node = root; 299 | 300 | for (var i = 1; i < pathCom.Length - 1; ++i) 301 | { 302 | if (node.Contains(pathCom[i]) && INode.Load(vfs, node.Find(pathCom[i])).IsDirectory()) 303 | { 304 | node = Load(vfs, node.Find(pathCom[i])); 305 | } 306 | else 307 | { 308 | return null; 309 | } 310 | } 311 | 312 | return node; 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的常规信息通过以下 6 | // 特性集控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("VirtualFileSystem")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("VirtualFileSystem")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 使此程序集中的类型 18 | // 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型, 19 | // 则将该类型上的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("197d295c-c134-45cf-9c88-0d69d79de5f7")] 24 | 25 | // 程序集的版本信息由下面四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.0 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace VirtualFileSystem.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的、缓存的 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("VirtualFileSystem.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 为所有资源查找重写当前线程的 CurrentUICulture 属性, 56 | /// 方法是使用此强类型资源类。 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace VirtualFileSystem.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /VirtualFileSystem/Properties/licenses.licx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bwbwbwbw/vfs/f8cfe3b0c2021436e8dea4c2eb3b3bfe8e8d472a/VirtualFileSystem/Properties/licenses.licx -------------------------------------------------------------------------------- /VirtualFileSystem/SuperBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace VirtualFileSystem 5 | { 6 | [StructLayout(LayoutKind.Sequential, Pack = 4)] 7 | public struct _SuperBlock 8 | { 9 | /// 10 | /// 可以容纳多少个 inode 11 | /// 12 | public UInt32 inodeCapacity; 13 | 14 | /// 15 | /// 已经分配了多少个 inode 16 | /// 17 | public UInt32 inodeAllocated; 18 | 19 | /// 20 | /// 每个数据块大小 21 | /// 22 | public UInt16 blockSize; 23 | 24 | /// 25 | /// 可以容纳多少个数据块 26 | /// 27 | public UInt32 blockCapacity; 28 | 29 | /// 30 | /// 已经预留了多少个数据块 31 | /// 32 | public UInt32 blockPreserved; 33 | 34 | /// 35 | /// 已经实际分配了多少个数据块 36 | /// 37 | public UInt32 blockAllocated; 38 | 39 | /// 40 | /// 有效标识符,必须为 0x1234 41 | /// 标识符放在最后,这样 SuperBlock 之前字段数量变更后就会要求重新格式化,避免潜在数据问题 42 | /// 43 | public UInt16 magicValue; 44 | 45 | /// 46 | /// 是否是一个有效的结构 47 | /// 48 | /// 49 | public Boolean IsValid() 50 | { 51 | return magicValue == 0x1234; 52 | } 53 | 54 | /// 55 | /// 初始化一个全新的 SuperBlock 56 | /// 57 | /// 58 | /// 59 | /// 60 | public _SuperBlock(UInt32 inodeCapacity,UInt16 blockSize, UInt32 blockCapacity) 61 | { 62 | this.inodeCapacity = inodeCapacity; 63 | this.inodeAllocated = 0; 64 | this.blockSize = blockSize; 65 | this.blockCapacity = blockCapacity; 66 | this.blockPreserved = 0; 67 | this.blockAllocated = 0; 68 | this.magicValue = 0x1234; 69 | } 70 | } 71 | 72 | public class SuperBlock 73 | { 74 | /// 75 | /// 可持久化数据 76 | /// 77 | public _SuperBlock data; 78 | 79 | /// 80 | /// inode 位图起始地址 81 | /// 82 | public UInt32 pInodeBitVectors; 83 | 84 | /// 85 | /// inode 区起始地址 86 | /// 87 | public UInt32 pInodeData; 88 | 89 | /// 90 | /// 数据块位图起始地址 91 | /// 92 | public UInt32 pBlockBitVectors; 93 | 94 | /// 95 | /// 数据块区起始地址 96 | /// 97 | public UInt32 pBlockData; 98 | 99 | /// 100 | /// 存储介质 101 | /// 102 | private VFSCore vfs; 103 | 104 | public SuperBlock(VFSCore vfs, _SuperBlock data) 105 | { 106 | this.vfs = vfs; 107 | this.data = data; 108 | 109 | if (data.IsValid()) 110 | { 111 | init(); 112 | } 113 | } 114 | 115 | /// 116 | /// 根据有效的持久化数据初始化整个结构 117 | /// 118 | private void init() 119 | { 120 | uint offset = 0; 121 | offset += (uint)Utils.GetStructSize<_SuperBlock>(); 122 | 123 | pInodeBitVectors = offset; 124 | offset += ((data.inodeCapacity / 32) + 1) * 4; 125 | 126 | pInodeData = offset; 127 | offset += data.inodeCapacity * (uint)Utils.GetStructSize<_INode>(); 128 | 129 | pBlockBitVectors = offset; 130 | offset += ((data.blockCapacity / 32) + 1) * 4; 131 | 132 | pBlockData = offset; 133 | } 134 | 135 | /// 136 | /// 将 superblock 写入存储介质 137 | /// 138 | /// 139 | public void Save() 140 | { 141 | vfs.GetDevice().Write(0, data); 142 | } 143 | 144 | /// 145 | /// 转换为字符串 146 | /// 147 | /// 148 | public override String ToString() 149 | { 150 | return String.Format("sizeof(_superBlock) = {8}, sizeof(_inode) = {9}, " + 151 | "inode 数 = {0}, 数据块大小 = {1} byte, 数据块数 = {2} (可容纳 {7} MB 数据), " + 152 | "p_inodeBitmap = {3}, p_inodeData = {4}, p_blockBitmap = {5}, p_blockData = {6}", 153 | data.inodeCapacity, data.blockSize, data.blockCapacity, 154 | pInodeBitVectors, pInodeData, pBlockBitVectors, pBlockData, 155 | data.blockSize * data.blockCapacity >> 20, 156 | Utils.GetStructSize<_SuperBlock>(), Utils.GetStructSize<_INode>()); 157 | } 158 | 159 | /// 160 | /// 从存储介质上还原 SuperBlock 161 | /// 162 | /// 163 | /// 164 | public static SuperBlock Load(VFSCore vfs) 165 | { 166 | var _superBlock = vfs.GetDevice().Read<_SuperBlock>(0); 167 | return new SuperBlock(vfs, _superBlock); 168 | } 169 | 170 | /// 171 | /// 创建一个全新的 SuperBlock 172 | /// 173 | /// 174 | /// 175 | /// 176 | /// 177 | /// 178 | public static SuperBlock Create(VFSCore vfs, UInt32 inodeCapacity, UInt16 blockSize, UInt32 blockCapacity) 179 | { 180 | var _superBlock = new _SuperBlock(inodeCapacity, blockSize, blockCapacity); 181 | var superBlock = new SuperBlock(vfs, _superBlock); 182 | superBlock.Save(); 183 | return superBlock; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /VirtualFileSystem/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace VirtualFileSystem 6 | { 7 | class Utils 8 | { 9 | /// 10 | /// 输出 16 进制数据 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static string HexDump(byte[] bytes, int bytesPerLine = 16) 16 | { 17 | if (bytes == null) return ""; 18 | int bytesLength = bytes.Length; 19 | 20 | char[] HexChars = "0123456789ABCDEF".ToCharArray(); 21 | 22 | int firstHexColumn = 23 | 8 // 8 characters for the address 24 | + 3; // 3 spaces 25 | 26 | int firstCharColumn = firstHexColumn 27 | + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space 28 | + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th 29 | + 2; // 2 spaces 30 | 31 | int lineLength = firstCharColumn 32 | + bytesPerLine // - characters to show the ascii value 33 | + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) 34 | 35 | char[] line = (new String(' ', lineLength - 2) + Environment.NewLine).ToCharArray(); 36 | int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; 37 | StringBuilder result = new StringBuilder(expectedLines * lineLength); 38 | 39 | for (int i = 0; i < bytesLength; i += bytesPerLine) 40 | { 41 | line[0] = HexChars[(i >> 28) & 0xF]; 42 | line[1] = HexChars[(i >> 24) & 0xF]; 43 | line[2] = HexChars[(i >> 20) & 0xF]; 44 | line[3] = HexChars[(i >> 16) & 0xF]; 45 | line[4] = HexChars[(i >> 12) & 0xF]; 46 | line[5] = HexChars[(i >> 8) & 0xF]; 47 | line[6] = HexChars[(i >> 4) & 0xF]; 48 | line[7] = HexChars[(i >> 0) & 0xF]; 49 | 50 | int hexColumn = firstHexColumn; 51 | int charColumn = firstCharColumn; 52 | 53 | for (int j = 0; j < bytesPerLine; j++) 54 | { 55 | if (j > 0 && (j & 7) == 0) hexColumn++; 56 | if (i + j >= bytesLength) 57 | { 58 | line[hexColumn] = ' '; 59 | line[hexColumn + 1] = ' '; 60 | line[charColumn] = ' '; 61 | } 62 | else 63 | { 64 | byte b = bytes[i + j]; 65 | line[hexColumn] = HexChars[(b >> 4) & 0xF]; 66 | line[hexColumn + 1] = HexChars[b & 0xF]; 67 | line[charColumn] = (b < 32 ? '·' : (char)b); 68 | } 69 | hexColumn += 3; 70 | charColumn++; 71 | } 72 | result.Append(line); 73 | } 74 | return result.ToString(); 75 | } 76 | 77 | /// 78 | /// 获取指定值类型的大小(sizeof) 79 | /// 80 | /// 81 | /// 82 | public static int GetStructSize() 83 | { 84 | Type tt = typeof(T); 85 | 86 | if (!tt.IsValueType) 87 | { 88 | throw new Exception("Only accept value type"); 89 | } 90 | 91 | return Marshal.SizeOf(tt); 92 | } 93 | 94 | /// 95 | /// 将结构体转换为字节数组 96 | /// 97 | /// 98 | /// 99 | /// 100 | public static byte[] StructToByte(T structure) where T : struct 101 | { 102 | int size = Marshal.SizeOf(structure); 103 | byte[] arr = new byte[size]; 104 | IntPtr ptr = Marshal.AllocHGlobal(size); 105 | 106 | Marshal.StructureToPtr(structure, ptr, true); 107 | Marshal.Copy(ptr, arr, 0, size); 108 | Marshal.FreeHGlobal(ptr); 109 | 110 | return arr; 111 | } 112 | 113 | /// 114 | /// 将字节数组转换为结构体 115 | /// 116 | /// 117 | /// 118 | /// 119 | public static T ByteToStruct(byte[] arr) where T : struct 120 | { 121 | T obj = new T(); 122 | 123 | int size = Marshal.SizeOf(obj); 124 | IntPtr ptr = Marshal.AllocHGlobal(size); 125 | 126 | Marshal.Copy(arr, 0, ptr, size); 127 | 128 | obj = (T)Marshal.PtrToStructure(ptr, obj.GetType()); 129 | Marshal.FreeHGlobal(ptr); 130 | 131 | return obj; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /VirtualFileSystem/VFS.Directory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace VirtualFileSystem 7 | { 8 | public partial class VFS 9 | { 10 | public class DirectoryInfo 11 | { 12 | public Boolean isDirectory { get; set; } 13 | 14 | public String name { get; set; } 15 | 16 | public String path { get; set; } 17 | 18 | public UInt32 size { get; set; } 19 | 20 | public UInt64 accessTime { get; set; } 21 | 22 | public UInt64 modifyTime { get; set; } 23 | 24 | public UInt64 creationTime { get; set; } 25 | 26 | public UInt32 flags { get; set; } 27 | 28 | public UInt32 owner { get; set; } 29 | 30 | public _INode inode { get; set; } 31 | 32 | public UInt32 inodeIndex { get; set; } 33 | } 34 | 35 | public class Directory 36 | { 37 | private VFSCore vfs; 38 | private INodeDirectory dir; 39 | 40 | private String path; 41 | 42 | public Directory(VFSCore vfs, String path) 43 | { 44 | this.vfs = vfs; 45 | 46 | if (!path.EndsWith("/")) 47 | { 48 | path += "/"; 49 | } 50 | 51 | this.path = path; 52 | 53 | dir = INodeDirectory.Resolve(vfs, path); 54 | 55 | if (dir == null) 56 | { 57 | throw new Exception("无效路径"); 58 | } 59 | } 60 | 61 | /// 62 | /// 创建一个文件夹 63 | /// 64 | /// 65 | public void CreateDirectory(String name) 66 | { 67 | VFS.AssertNameValid(name); 68 | 69 | INode inode = vfs.AllocateINode(1, 2333); 70 | if (!dir.Add(name, new INodeDirectory(vfs, inode))) 71 | { 72 | throw new Exception("创建文件夹失败"); 73 | } 74 | } 75 | 76 | /// 77 | /// 目录下是否包含一个文件或目录 78 | /// 79 | /// 80 | /// 81 | public Boolean Contains(String name) 82 | { 83 | return dir.Contains(name); 84 | } 85 | 86 | /// 87 | /// 列出一个目录下所有内容 88 | /// 89 | /// 90 | public List List() 91 | { 92 | var ret = new List(); 93 | var entries = dir.List(); 94 | foreach (var entry in entries) 95 | { 96 | var info = new DirectoryInfo(); 97 | INode inode = INode.Load(vfs, entry.Value); 98 | info.isDirectory = inode.IsDirectory(); 99 | info.accessTime = inode.data.accessTime; 100 | info.creationTime = inode.data.creationTime; 101 | info.flags = inode.data.flags; 102 | info.modifyTime = inode.data.modifyTime; 103 | info.name = entry.Key; 104 | info.path = this.path + entry.Key; 105 | info.owner = inode.data.owner; 106 | info.size = inode.data.sizeByte; 107 | info.inode = inode.data; 108 | info.inodeIndex = inode.index; 109 | ret.Add(info); 110 | } 111 | return ret; 112 | } 113 | 114 | /// 115 | /// 重命名文件或文件夹 116 | /// 117 | /// 118 | /// 119 | public void Rename(String oldName, String newName) 120 | { 121 | VFS.AssertNameValid(oldName); 122 | VFS.AssertNameValid(newName); 123 | 124 | if (!dir.Contains(oldName)) 125 | { 126 | throw new Exception("文件或文件夹未找到"); 127 | } 128 | if (dir.Contains(newName)) 129 | { 130 | throw new Exception("新文件名与现有文件或文件夹名称冲突"); 131 | } 132 | if (!dir.Rename(oldName, newName)) 133 | { 134 | throw new Exception("重命名失败"); 135 | } 136 | } 137 | 138 | /// 139 | /// 删除文件或文件夹 140 | /// 141 | /// 142 | public void Delete(String name) 143 | { 144 | if (!dir.Contains(name)) 145 | { 146 | throw new Exception("文件或文件夹未找到"); 147 | } 148 | if (!dir.Delete(name)) 149 | { 150 | throw new Exception("删除失败"); 151 | } 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /VirtualFileSystem/VFS.File.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace VirtualFileSystem 7 | { 8 | public partial class VFS 9 | { 10 | public enum FileMode 11 | { 12 | /// 13 | /// 如果文件已存在,则将引发 IO 异常。 14 | /// 15 | CreateNew = 1, 16 | 17 | /// 18 | /// 如果文件已存在,它将被覆盖。 19 | /// 20 | Create = 2, 21 | 22 | /// 23 | /// 指定操作系统应打开现有文件。如果该文件不存在,则引发文件未找到异常。 24 | /// 25 | Open = 3, 26 | 27 | /// 28 | /// 指定操作系统应打开文件(如果文件存在);否则,应创建新文件。 29 | /// 30 | OpenOrCreate = 4, 31 | 32 | /// 33 | /// 指定操作系统应打开现有文件。文件一旦打开,就将被截断为零字节大小。 34 | /// 35 | Truncate = 5, 36 | 37 | /// 38 | /// 若存在文件,则打开该文件并查找到文件尾,或者创建一个新文件。 39 | /// 40 | Append = 6 41 | } 42 | 43 | public class File 44 | { 45 | public UInt32 position = 0; 46 | public String name; 47 | 48 | private VFSCore vfs; 49 | private INode inode; 50 | 51 | public File(VFSCore vfs, String path, FileMode fileMode) 52 | { 53 | this.vfs = vfs; 54 | 55 | var directory = VFS.GetPathDirectory(path); 56 | var name = VFS.GetPathName(path); 57 | VFS.AssertNameValid(name); 58 | 59 | INodeDirectory dir = INodeDirectory.Resolve(vfs, directory); 60 | if (dir == null) 61 | { 62 | throw new Exception("无法访问目录"); 63 | } 64 | switch (fileMode) 65 | { 66 | case FileMode.CreateNew: 67 | if (dir.Contains(name)) 68 | { 69 | throw new Exception("文件已存在"); 70 | } 71 | CreateFile(dir, name); 72 | break; 73 | case FileMode.Create: 74 | if (dir.Contains(name)) 75 | { 76 | OpenFile(dir, name); 77 | inode.Resize(0); 78 | } 79 | else 80 | { 81 | CreateFile(dir, name); 82 | } 83 | break; 84 | case FileMode.Open: 85 | if (!dir.Contains(name)) 86 | { 87 | throw new Exception("文件未找到"); 88 | } 89 | OpenFile(dir, name); 90 | break; 91 | case FileMode.OpenOrCreate: 92 | if (dir.Contains(name)) 93 | { 94 | OpenFile(dir, name); 95 | } 96 | else 97 | { 98 | CreateFile(dir, name); 99 | } 100 | break; 101 | case FileMode.Truncate: 102 | if (!dir.Contains(name)) 103 | { 104 | throw new Exception("文件未找到"); 105 | } 106 | OpenFile(dir, name); 107 | inode.Resize(0); 108 | break; 109 | case FileMode.Append: 110 | if (!dir.Contains(name)) 111 | { 112 | CreateFile(dir, name); 113 | } 114 | else 115 | { 116 | OpenFile(dir, name); 117 | position = inode.data.sizeByte; 118 | } 119 | break; 120 | default: 121 | throw new ArgumentException(); 122 | } 123 | } 124 | 125 | /// 126 | /// 返回文件大小 127 | /// 128 | /// 129 | public UInt32 Size() 130 | { 131 | return inode.data.sizeByte; 132 | } 133 | 134 | /// 135 | /// 通过创建一个新文件来初始化该类 136 | /// 137 | /// 138 | /// 139 | /// 140 | private void CreateFile(INodeDirectory dir, String name) 141 | { 142 | inode = vfs.AllocateINode(0, 2333); 143 | dir.Add(name, inode); 144 | } 145 | 146 | /// 147 | /// 通过打开现有文件来初始化该类 148 | /// 149 | /// 150 | /// 151 | private void OpenFile(INodeDirectory dir, String name) 152 | { 153 | inode = INode.Load(vfs, dir.Find(name)); 154 | } 155 | 156 | /// 157 | /// 移动文件指针 158 | /// 159 | /// 160 | public void Seek(UInt32 position) 161 | { 162 | this.position = position; 163 | } 164 | 165 | /// 166 | /// 写入字节数据 167 | /// 168 | /// 169 | public void Write(byte[] array) 170 | { 171 | Write(array, 0, (uint)array.Length); 172 | } 173 | 174 | /// 175 | /// 写入字节数据 176 | /// 177 | /// 178 | /// 179 | /// 180 | public void Write(byte[] array, UInt32 offset, UInt32 count) 181 | { 182 | byte[] arr = new byte[count]; 183 | Buffer.BlockCopy(array, (int)offset, arr, 0, (int)count); 184 | inode.Write(position, arr); 185 | position += count; 186 | } 187 | 188 | /// 189 | /// 读取从当前位置开始所有数据 190 | /// 191 | /// 192 | /// 193 | public UInt32 Read(byte[] array) 194 | { 195 | if (position >= inode.data.sizeByte) 196 | { 197 | return 0; 198 | } 199 | var count = inode.data.sizeByte - position; 200 | inode.Read(position, array, count); 201 | position += count; 202 | return count; 203 | } 204 | 205 | /// 206 | /// 读取字节数据 207 | /// 208 | /// 209 | /// 210 | /// 211 | /// 212 | public UInt32 Read(byte[] array, UInt32 offset, UInt32 count) 213 | { 214 | if (position >= inode.data.sizeByte) 215 | { 216 | return 0; 217 | } 218 | if (position + count > inode.data.sizeByte) 219 | { 220 | count = inode.data.sizeByte - position; 221 | } 222 | byte[] arr = new byte[count]; 223 | inode.Read(position, arr, count); 224 | Buffer.BlockCopy(arr, 0, array, (int)offset, (int)count); 225 | position += count; 226 | return count; 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /VirtualFileSystem/VFS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using VirtualFileSystem.DeviceAdapter; 6 | 7 | namespace VirtualFileSystem 8 | { 9 | public partial class VFS 10 | { 11 | public VFSCore vfs; 12 | 13 | public VFS(AbstractDevice device) 14 | { 15 | vfs = new VFSCore(device); 16 | } 17 | 18 | public _SuperBlock GetSuperBlock() 19 | { 20 | return vfs.GetSuperBlock().data; 21 | } 22 | 23 | public DeviceAdapter.AbstractDevice GetDevice() 24 | { 25 | return vfs.GetDevice(); 26 | } 27 | 28 | public Directory NewDirectory(String path) 29 | { 30 | return new Directory(vfs, path); 31 | } 32 | 33 | public File NewFile(String path, FileMode fileMode) 34 | { 35 | return new File(vfs, path, fileMode); 36 | } 37 | 38 | /// 39 | /// 是否已格式化 40 | /// 41 | /// 42 | public Boolean IsFormated() 43 | { 44 | return vfs.IsFormated(); 45 | } 46 | 47 | /// 48 | /// 格式化磁盘 49 | /// 50 | /// 51 | /// 52 | public void Format(UInt32 inodeCapacity, UInt16 blockSizeKB) 53 | { 54 | vfs.Format(inodeCapacity, blockSizeKB); 55 | } 56 | 57 | /// 58 | /// 判断一个路径是否有效 59 | /// 60 | /// 61 | /// 62 | public static Boolean IsPathValid(String path) 63 | { 64 | if (path.Length == 0) 65 | { 66 | return false; 67 | } 68 | if (path.ElementAt(0) != '/') 69 | { 70 | return false; 71 | } 72 | return true; 73 | } 74 | 75 | /// 76 | /// 判断一个文件名是否有效 77 | /// 78 | /// 79 | /// 80 | public static Boolean IsNameValid(String name) 81 | { 82 | if (name.Length == 0) 83 | { 84 | return false; 85 | } 86 | if (name.Contains('/')) 87 | { 88 | return false; 89 | } 90 | if (name == "." || name == "..") 91 | { 92 | return false; 93 | } 94 | if (name.EndsWith(".")) 95 | { 96 | return false; 97 | } 98 | 99 | return true; 100 | } 101 | 102 | /// 103 | /// 文件名无效时抛出异常 104 | /// 105 | /// 106 | public static void AssertNameValid(String name) 107 | { 108 | if (!IsNameValid(name)) 109 | { 110 | throw new Exception("无效文件名"); 111 | } 112 | } 113 | 114 | /// 115 | /// 路径无效时抛出异常 116 | /// 117 | /// 118 | public static void AssertPathValid(String path) 119 | { 120 | if (!IsPathValid(path)) 121 | { 122 | throw new Exception("无效路径"); 123 | } 124 | } 125 | 126 | /// 127 | /// 获取一个路径的文件名 128 | /// 129 | /// 130 | /// 131 | public static String GetPathName(String path) 132 | { 133 | AssertPathValid(path); 134 | var pos = path.LastIndexOf('/'); 135 | return path.Substring(pos + 1); 136 | } 137 | 138 | /// 139 | /// 获取一个路径的目录 140 | /// 141 | /// 142 | /// 143 | public static String GetPathDirectory(String path) 144 | { 145 | AssertPathValid(path); 146 | var pos = path.LastIndexOf('/'); 147 | return path.Substring(0, pos + 1); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /VirtualFileSystem/VFSCore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using VirtualFileSystem.DeviceAdapter; 4 | 5 | namespace VirtualFileSystem 6 | { 7 | public class VFSCore 8 | { 9 | /// 10 | /// 存储介质大小(字节) 11 | /// 12 | private UInt32 deviceSize; 13 | 14 | /// 15 | /// 存储介质对象 16 | /// 17 | private AbstractDevice device; 18 | 19 | /// 20 | /// 超级块对象 21 | /// 22 | private SuperBlock superBlock; 23 | 24 | /// 25 | /// inode 位图 26 | /// 27 | private UInt32[] inodeBitmaps; 28 | 29 | /// 30 | /// 数据块位图 31 | /// 32 | private UInt32[] blockBitmaps; 33 | 34 | public VFSCore(AbstractDevice device) 35 | { 36 | this.device = device; 37 | this.deviceSize = device.Size(); 38 | 39 | // 初始化超级块 40 | this.superBlock = SuperBlock.Load(this); 41 | 42 | if (IsFormated()) 43 | { 44 | loadBitmaps(); 45 | Console.WriteLine("载入镜像成功。"); 46 | Console.WriteLine("SuperBlock: {0}", superBlock.ToString()); 47 | } 48 | } 49 | 50 | /// 51 | /// 获取此文件系统的存储介质操作对象 52 | /// 53 | /// 54 | public AbstractDevice GetDevice() 55 | { 56 | return device; 57 | } 58 | 59 | /// 60 | /// 获取此文件系统的超级块操作对象 61 | /// 62 | /// 63 | public SuperBlock GetSuperBlock() 64 | { 65 | return superBlock; 66 | } 67 | 68 | /// 69 | /// 返回存储介质是否已格式化 70 | /// 71 | /// 72 | public Boolean IsFormated() 73 | { 74 | return superBlock.data.IsValid(); 75 | } 76 | 77 | /// 78 | /// 从已经格式化的存储介质中载入位图到内存 79 | /// 80 | private void loadBitmaps() 81 | { 82 | int inodeVectorCount = (int)superBlock.data.inodeCapacity / 32 + 1; 83 | inodeBitmaps = device.ReadArray(superBlock.pInodeBitVectors, inodeVectorCount); 84 | 85 | int blockVectorCount = (int)superBlock.data.blockCapacity / 32 + 1; 86 | blockBitmaps = device.ReadArray(superBlock.pBlockBitVectors, blockVectorCount); 87 | } 88 | 89 | /// 90 | /// 格式化存储介质 91 | /// 92 | /// inode 数量 93 | /// block 大小(KB),必须为 1,2,4,8 中的一个 94 | public void Format(UInt32 inodeCapacity, UInt16 blockSizeKByte = 4) 95 | { 96 | if (inodeCapacity < 32) 97 | { 98 | throw new Exception("inode 至少为 32 个"); 99 | } 100 | 101 | if (!new int[] { 1, 2, 4, 8 }.Contains(blockSizeKByte)) 102 | { 103 | throw new Exception("block 大小只能为 1KB, 2KB, 4KB 或 8KB"); 104 | } 105 | 106 | uint offset = 0; 107 | offset += (uint)Utils.GetStructSize<_SuperBlock>(); 108 | offset += (inodeCapacity / 32 + 1) * 4; 109 | offset += (uint)Utils.GetStructSize<_INode>() * inodeCapacity; 110 | 111 | if (offset > deviceSize) 112 | { 113 | throw new Exception("inode 数量过大,结构超出存储介质最大空间"); 114 | } 115 | 116 | // 可留给数据块位图和数据块区的大小 117 | uint sizeRemain = deviceSize - offset; 118 | 119 | // 全部留给数据块,可以有多少个数据块 120 | uint blockCapacity = sizeRemain / blockSizeKByte >> 10; 121 | 122 | if (blockCapacity < 128) 123 | { 124 | throw new Exception("磁盘空间太小,至少要可以容纳 128 个块"); 125 | } 126 | 127 | // 删除 (blockCapacity / 32 + 1) * 4 大小的数据块,留作数据块位图使用 128 | blockCapacity -= ((blockCapacity / 32 + 1) * 4 / blockSizeKByte) + 1; 129 | 130 | // 初始化 superBlock 131 | superBlock = SuperBlock.Create(this, inodeCapacity, (UInt16)(blockSizeKByte << 10), blockCapacity); 132 | 133 | // 单个 inode BitVector 可容纳 32 位 134 | inodeBitmaps = new UInt32[inodeCapacity / 32 + 1]; 135 | device.WriteArray(superBlock.pInodeBitVectors, inodeBitmaps, 0, inodeBitmaps.Length); 136 | 137 | // 单个 block BitVector 可容纳 32 位 138 | blockBitmaps = new UInt32[blockCapacity / 32 + 1]; 139 | device.WriteArray(superBlock.pBlockBitVectors, blockBitmaps, 0, blockBitmaps.Length); 140 | 141 | // 写入根目录 142 | INodeDirectory.Create(this); 143 | 144 | Console.WriteLine("格式化成功。"); 145 | Console.WriteLine("SuperBlock: {0}", superBlock.ToString()); 146 | } 147 | 148 | /// 149 | /// 预留指定数量的数据块 150 | /// 151 | /// 152 | public void PreserveBlock(UInt32 blocksToPreserve) 153 | { 154 | if (blocksToPreserve == 0) 155 | { 156 | return; 157 | } 158 | if (superBlock.data.blockPreserved + blocksToPreserve > superBlock.data.blockCapacity) 159 | { 160 | throw new Exception("block 数量已满"); 161 | } 162 | superBlock.data.blockPreserved += blocksToPreserve; 163 | superBlock.Save(); 164 | } 165 | 166 | /// 167 | /// 减少预留指定数量的数据块 168 | /// 169 | /// 170 | public void DePreserveBlock(UInt32 blocksToDePreserve) 171 | { 172 | if (blocksToDePreserve == 0) 173 | { 174 | return; 175 | } 176 | if (superBlock.data.blockPreserved < blocksToDePreserve) 177 | { 178 | throw new Exception("block 不足"); 179 | } 180 | superBlock.data.blockPreserved -= blocksToDePreserve; 181 | superBlock.Save(); 182 | } 183 | 184 | /// 185 | /// 位图指定位置是否已被分配 186 | /// 187 | /// 188 | /// 189 | /// 190 | private Boolean IsBitmapAllocated(UInt32[] bitmap, UInt32 index) 191 | { 192 | if ((bitmap[index / 32] & (uint)1 << (int)(index % 32)) == 0) 193 | { 194 | return false; 195 | } 196 | else 197 | { 198 | return true; 199 | } 200 | } 201 | 202 | /// 203 | /// 获取一个位图可用的位置,若无法找到则返回 Int32.MaxValue 204 | /// 205 | /// 206 | /// 207 | private UInt32 GitBitmapAvailableIndex(UInt32[] bitmap) 208 | { 209 | for (int i = 0; i < bitmap.Length; ++i) 210 | { 211 | if (bitmap[i] != uint.MaxValue) 212 | { 213 | for (int j = 0; j < 32; ++j) 214 | { 215 | if ((bitmap[i] & (1 << j)) == 0) 216 | { 217 | return (UInt32)(i * 32 + j); 218 | } 219 | } 220 | } 221 | } 222 | 223 | return UInt32.MaxValue; 224 | } 225 | 226 | /// 227 | /// 持久化一个位图向量 228 | /// 229 | /// 230 | /// 231 | /// 232 | private void UpdateBitmapVectorAtIndex(UInt32 offset, UInt32[] bitmap, UInt32 index) 233 | { 234 | device.Write(offset + index / 32 * 4, bitmap[index / 32]); 235 | } 236 | 237 | /// 238 | /// 位图指定位置标记为已占用 239 | /// 240 | /// 241 | /// 242 | /// 243 | private void SetBitmapAllocated(UInt32 offset, UInt32[] bitmap, UInt32 index) 244 | { 245 | bitmap[index / 32] |= (uint)1 << (int)(index % 32); 246 | UpdateBitmapVectorAtIndex(offset, bitmap, index); 247 | } 248 | 249 | /// 250 | /// 位图指定位置标记为未被占用 251 | /// 252 | /// 253 | /// 254 | /// 255 | private void SetBitmapNotAllocated(UInt32 offset, UInt32[] bitmap, UInt32 index) 256 | { 257 | bitmap[index / 32] &= ~((uint)1 << (int)(index % 32)); 258 | UpdateBitmapVectorAtIndex(offset, bitmap, index); 259 | } 260 | 261 | /// 262 | /// 判断 inode index 是否已被占用 263 | /// 264 | /// 265 | /// 266 | public Boolean IsINodeAllocated(UInt32 index) 267 | { 268 | if (index == UInt32.MaxValue) 269 | { 270 | return false; 271 | } 272 | return IsBitmapAllocated(inodeBitmaps, index); 273 | } 274 | 275 | /// 276 | /// 判断 block inde 277 | /// 278 | /// 279 | /// 280 | public Boolean IsBlockAllocated(UInt32 index) 281 | { 282 | if (index == UInt32.MaxValue) 283 | { 284 | return false; 285 | } 286 | return IsBitmapAllocated(blockBitmaps, index); 287 | } 288 | 289 | /// 290 | /// 查找一个空闲 inode 291 | /// 292 | /// 293 | private UInt32 GetFreeINodeIndex() 294 | { 295 | return GitBitmapAvailableIndex(inodeBitmaps); 296 | } 297 | 298 | /// 299 | /// 查找一个空闲块 300 | /// 301 | /// 302 | private UInt32 GetFreeBlockIndex() 303 | { 304 | return GitBitmapAvailableIndex(blockBitmaps); 305 | } 306 | 307 | /// 308 | /// 分配、初始化一个新的 inode 309 | /// 310 | /// 311 | public INode AllocateINode(UInt32 flags, UInt32 owner) 312 | { 313 | // 查找位图,寻找一个可用的 index 314 | UInt32 freeIndex = GetFreeINodeIndex(); 315 | if (freeIndex == UInt32.MaxValue) 316 | { 317 | throw new Exception("inode 数量已满"); 318 | } 319 | 320 | // 创建 321 | INode inode = INode.Create(this, freeIndex, flags, owner); 322 | 323 | // 置位 324 | SetBitmapAllocated(superBlock.pInodeBitVectors, inodeBitmaps, freeIndex); 325 | 326 | // 更新计数器 327 | superBlock.data.inodeAllocated++; 328 | superBlock.Save(); 329 | 330 | return inode; 331 | } 332 | 333 | /// 334 | /// 分配一个新数据块并清空内容 335 | /// 336 | /// 337 | public Block AllocateBlock(Byte fill = 0) 338 | { 339 | // 查找位图,寻找一个可用的 index 340 | UInt32 freeIndex = GetFreeBlockIndex(); 341 | if (freeIndex == UInt32.MaxValue) 342 | { 343 | throw new Exception("block 数量已满"); 344 | } 345 | 346 | // 创建 347 | Block block = Block.Create(this, freeIndex, fill); 348 | 349 | // 置位 350 | SetBitmapAllocated(superBlock.pBlockBitVectors, blockBitmaps, freeIndex); 351 | 352 | // 更新计数器 353 | superBlock.data.blockAllocated++; 354 | superBlock.Save(); 355 | 356 | return block; 357 | } 358 | 359 | /// 360 | /// 收回一个 inode 361 | /// 362 | /// 363 | public void DeAllocateINode(UInt32 inodeIndex) 364 | { 365 | if (inodeIndex >= superBlock.data.inodeCapacity) 366 | { 367 | return; 368 | } 369 | if (!IsINodeAllocated(inodeIndex)) 370 | { 371 | return; 372 | } 373 | 374 | // 位清零 375 | SetBitmapNotAllocated(superBlock.pInodeBitVectors, inodeBitmaps, inodeIndex); 376 | 377 | // 更新计数器 378 | superBlock.data.inodeAllocated--; 379 | superBlock.Save(); 380 | } 381 | 382 | /// 383 | /// 收回一个数据块 384 | /// 385 | /// 386 | public void DeAllocateBlock(UInt32 blockIndex) 387 | { 388 | if (blockIndex == UInt32.MaxValue) 389 | { 390 | Console.WriteLine("Warn: Deallocating NULL block"); 391 | return; 392 | } 393 | if (blockIndex >= superBlock.data.blockCapacity) 394 | { 395 | Console.WriteLine("Warn: Deallocating block out of bound"); 396 | return; 397 | } 398 | if (!IsBlockAllocated(blockIndex)) 399 | { 400 | Console.WriteLine("Warn: Deallocating block {0} which is not allocated", blockIndex); 401 | return; 402 | } 403 | 404 | // 位清零 405 | SetBitmapNotAllocated(superBlock.pBlockBitVectors, blockBitmaps, blockIndex); 406 | 407 | // 更新计数器 408 | superBlock.data.blockAllocated--; 409 | superBlock.data.blockPreserved--; 410 | superBlock.Save(); 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /VirtualFileSystem/VirtualFileSystem.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {362E0E54-3B00-4C19-8F58-90F8E5430F95} 8 | Library 9 | Properties 10 | VirtualFileSystem 11 | VirtualFileSystem 12 | v4.0 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x86\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x86 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | 43 | 44 | bin\x86\Release\ 45 | TRACE 46 | true 47 | pdbonly 48 | x86 49 | prompt 50 | MinimumRecommendedRules.ruleset 51 | 52 | 53 | 54 | 55 | 56 | 57 | ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll 58 | True 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | ResXFileCodeGenerator 88 | Resources.Designer.cs 89 | Designer 90 | 91 | 92 | True 93 | Resources.resx 94 | 95 | 96 | 97 | SettingsSingleFileGenerator 98 | Settings.Designer.cs 99 | 100 | 101 | True 102 | Settings.settings 103 | True 104 | 105 | 106 | 107 | 114 | -------------------------------------------------------------------------------- /VirtualFileSystem/WeakValueDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if !CompactFramework 3 | using System.Runtime.Serialization; 4 | #endif 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Diagnostics; 8 | 9 | namespace VirtualFileSystem 10 | { 11 | /// 12 | /// A dictionary in which the values are weak references. Written by DLP for SWIG. 13 | /// 14 | /// 15 | /// Null values are not allowed in this dictionary. 16 | /// 17 | /// When a value is garbage-collected, the dictionary acts as though the key is 18 | /// not present. 19 | /// 20 | /// This class "cleans up" periodically by removing entries with garbage-collected 21 | /// values. Cleanups only occur occasionally, and only when the dictionary is accessed; 22 | /// Accessing it (for read or write) more often results in more frequent cleanups. 23 | /// 24 | /// Watch out! The following interface members are not implemented: 25 | /// IDictionary.Values, ICollection.Contains, ICollection.CopyTo, ICollection.Remove. 26 | /// Also, the dictionary is NOT MULTITHREAD-SAFE. 27 | /// 28 | public class WeakValueDictionary : IDictionary 29 | where TValue : class 30 | { 31 | Dictionary> _dict = new Dictionary>(); 32 | int _version, _cleanVersion; 33 | #if !CompactFramework 34 | int _cleanGeneration; 35 | #endif 36 | const int MinRehashInterval = 500; 37 | 38 | public WeakValueDictionary() 39 | { 40 | } 41 | 42 | #region IDictionary Members 43 | 44 | public ICollection Keys 45 | { 46 | get { return _dict.Keys; } 47 | } 48 | public ICollection Values 49 | { // TODO. Maybe. Eventually. 50 | get { throw new NotImplementedException(); } 51 | } 52 | 53 | public bool ContainsKey(TKey key) 54 | { 55 | AutoCleanup(1); 56 | 57 | WeakReference value; 58 | if (!_dict.TryGetValue(key, out value)) 59 | return false; 60 | return value.IsAlive; 61 | } 62 | public void Add(TKey key, TValue value) 63 | { 64 | AutoCleanup(2); 65 | 66 | WeakReference wr; 67 | if (_dict.TryGetValue(key, out wr)) 68 | { 69 | if (wr.IsAlive) 70 | throw new ArgumentException("An element with the same key already exists in this WeakValueDictionary"); 71 | else 72 | wr.Target = value; 73 | } 74 | else 75 | _dict.Add(key, new WeakReference(value)); 76 | } 77 | public bool Remove(TKey key) 78 | { 79 | AutoCleanup(1); 80 | 81 | WeakReference wr; 82 | if (!_dict.TryGetValue(key, out wr)) 83 | return false; 84 | _dict.Remove(key); 85 | return wr.IsAlive; 86 | } 87 | public bool TryGetValue(TKey key, out TValue value) 88 | { 89 | AutoCleanup(1); 90 | 91 | WeakReference wr; 92 | if (_dict.TryGetValue(key, out wr)) 93 | value = wr.Target; 94 | else 95 | value = null; 96 | return value != null; 97 | } 98 | 99 | public TValue this[TKey key] 100 | { 101 | get 102 | { 103 | return _dict[key].Target; 104 | } 105 | set 106 | { 107 | _dict[key] = new WeakReference(value); 108 | } 109 | } 110 | 111 | void AutoCleanup(int incVersion) 112 | { 113 | _version += incVersion; 114 | 115 | // Cleanup the table every so often--less often for larger tables. 116 | long delta = _version - _cleanVersion; 117 | if (delta > MinRehashInterval + _dict.Count) 118 | { 119 | #if CompactFramework 120 | Cleanup(); 121 | _cleanVersion = _version; 122 | #else 123 | // A cleanup will be fruitless unless a GC has happened in the meantime. 124 | // WeakReferences can become zero only during the GC. 125 | int curGeneration = GC.CollectionCount(0); 126 | if (_cleanGeneration != curGeneration) 127 | { 128 | _cleanGeneration = curGeneration; 129 | Cleanup(); 130 | _cleanVersion = _version; 131 | } 132 | else 133 | _cleanVersion += MinRehashInterval; // Wait a little while longer 134 | #endif 135 | } 136 | } 137 | void Cleanup() 138 | { 139 | // Remove all pairs whose value is nullified. 140 | // Due to the fact that you can't change a Dictionary while enumerating 141 | // it, we need an intermediate collection (the list of things to delete): 142 | List deadKeys = new List(); 143 | 144 | foreach (KeyValuePair> kvp in _dict) 145 | if (!kvp.Value.IsAlive) 146 | deadKeys.Add(kvp.Key); 147 | 148 | foreach (TKey key in deadKeys) 149 | { 150 | bool success = _dict.Remove(key); 151 | Debug.Assert(success); 152 | } 153 | } 154 | #endregion 155 | 156 | #region ICollection> Members 157 | 158 | public void Add(KeyValuePair item) 159 | { 160 | Add(item.Key, item.Value); 161 | } 162 | public void Clear() 163 | { 164 | _dict.Clear(); 165 | _version = _cleanVersion = 0; 166 | #if !CompactFramework 167 | _cleanGeneration = 0; 168 | #endif 169 | } 170 | public bool Contains(KeyValuePair item) 171 | { 172 | throw new Exception("The method or operation is not implemented."); 173 | } 174 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 175 | { 176 | throw new Exception("The method or operation is not implemented."); 177 | } 178 | public int Count 179 | { 180 | // THIS VALUE MAY BE WRONG (i.e. it may be higher than the number of 181 | // items you get from the iterator). 182 | get { return _dict.Count; } 183 | } 184 | public bool IsReadOnly 185 | { 186 | get { return false; } 187 | } 188 | public bool Remove(KeyValuePair item) 189 | { 190 | throw new Exception("The method or operation is not implemented."); 191 | } 192 | 193 | #endregion 194 | 195 | #region IEnumerable> Members 196 | 197 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } 198 | public IEnumerator> GetEnumerator() 199 | { 200 | int nullCount = 0; 201 | 202 | foreach (KeyValuePair> kvp in _dict) 203 | { 204 | TValue target = kvp.Value.Target; 205 | if (target == null) 206 | nullCount++; 207 | else 208 | yield return new KeyValuePair(kvp.Key, target); 209 | } 210 | 211 | if (nullCount > _dict.Count / 4) 212 | Cleanup(); 213 | } 214 | 215 | #endregion 216 | } 217 | 218 | public class WeakReference : System.WeakReference 219 | { 220 | public WeakReference(T target) : base(target) { } 221 | public WeakReference(T target, bool trackResurrection) : base(target, trackResurrection) { } 222 | #if !CompactFramework 223 | protected WeakReference(SerializationInfo info, StreamingContext context) : base(info, context) { } 224 | #endif 225 | public new T Target 226 | { 227 | get { return (T)base.Target; } 228 | set { base.Target = value; } 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /VirtualFileSystem/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------