├── .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 |
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 |
--------------------------------------------------------------------------------