一、前言
河北大学2019级软件工程专业王运齐的操作系统课程设计
二、设计要求
2.1 设计题目
基于位示图算法的连接文件管理系统
2.2 内容摘要
根据操作系统理论课上学习的操作系统中关于文件管理实现方法,在采用位示图算法的连接文件基础上模拟实现操作系统的文件管理功能和用户接口。
三、系统实现
3.1 磁盘管理
采用位示图的方法管理磁盘的分配与回收,将磁盘分成128个大小相等的磁盘块。将文件分配表与位示图组合在一起,共同占用第0、1号磁盘块,当文件分配表中表项的值为-1时代表该磁盘块被占用。
3.1.1磁盘的初始化
系统占用三个磁盘块,占用时显示为蓝色,空闲磁盘块显示为橙色,一共有128块磁盘块,空闲时赋值为0,占用时赋值为-1,如下图3-1所示。
图3-1 初始化的磁盘
/**
* 初始化模拟磁盘
*/
static void initDisk() {
File file = new File(OsConstant.DISK_FILE);
FileOutputStream fout = null;
//判断模拟磁盘是否已经创建
if (!file.exists()) {
try {
fout = new FileOutputStream(file);
byte[] bytes;
//封装魔法值,128块磁盘
for (int i = 0; i < DISK_BLOCK_QUNTITY; i++) {
//磁盘块大小为64
bytes = new byte[DISK_BLOCK_SIZE];
//写入初始文件分配表
if (i == 0) {
//前三个盘块不可用
bytes[0] = -1;
bytes[1] = -1;
bytes[2] = -1;
}
//写入根目录
if (i == 2) {
bytes[0] = 'r';//根目录名为root
bytes[1] = 'o';
bytes[2] = 'o';
bytes[3] = 't';
bytes[4] = 0;
bytes[5] = Byte.parseByte("00001000", 2);//目录属性
bytes[6] = -1;//起始盘号
bytes[7] = 0;//保留一字节未使用
}
fout.write(bytes);
}
} catch (FileNotFoundException e) {
java.lang.System.out.println("打开/新建磁盘文件失败!");
e.printStackTrace();
java.lang.System.exit(0);
} catch (IOException e) {
java.lang.System.out.println("写入文件时发生错误");
e.printStackTrace();
java.lang.System.exit(0);
} finally {
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
java.lang.System.out.println("关闭文件流时发生错误");
e.printStackTrace();
}
}
}
} else {
System.out.println("模拟磁盘已存在,无需重新创建");
}
}
/**
* 更新磁盘使用情况
*/
public void updateFatView() throws IOException {
byte[] fat = OS.fileOperator.getFat();
for (int i = 0; i < fat.length; i++) {
Pane pane = (Pane) fatView.getChildren().get(i);
//设置磁盘颜色
if (fat[i] != 0) {
//占用时为蓝色
pane.setStyle("-fx-background-color: #2a3cff;-fx-border-color: #EEEEBB");
} else {
//空闲时为橙色
pane.setStyle("-fx-background-color:coral;-fx-border-color: #EEEEBB");
}
}
}
3.1.2磁盘的分配
根据进程页数得到其所需的物理块数,检查空闲物理块总数是否足够,不能满足则分配失败;能满足该进程的需求,则查位示图中为“0”的位,计算出物理块号,并写入进程的页表。位示图中第i个字的第j位对应的物理块号为:块号=iX位示图中的字长+j。系统占用前两个磁盘块,第三个磁盘块存放根目录root,文件aaa,bbb,ccc分别占用第四五六块。显示为蓝色空闲磁盘块显示为橙色,如下图3-2所示:
图3-2 磁盘块的分配
3.1.3磁盘的回收
当归还一块时,若归还的是文件,则查找该文件的文件控制块FCB中存储的起始盘块号,依次扫描文件分配表FAT,记录下文件的磁盘块号,直到文件分配表FAT中存储的内容为253,再修改FAT中对应磁盘块的内容为0;若归还的是目录的磁盘块,则查找该目录的文件控制块FCB中存储的起始盘块号,将该盘块中存储的文件和目录依次归还,再归还该目录的磁盘块,将FAT中对应的表项修改为0。对于盘块的回收,主要完成两个内容:将回收盘块的盘块号转换成位示图中的行号和列号,并且修改位示图。
3.2目录结构
采用链式结构,根目录目录下包含子目录与文件。目录内容包括目录项占用空间、文件名、目录/文件属性、目录/文件起始盘块号、文件长度(单位为盘块,如果是目录则为0)、文件内容,如下图3-3所示:
图3-3 目录结构1
3.2.1构建目录树
/**
* 构建目录树
*/
public void initCatalogTree() throws Exception {
Catalog root = OS.fileOperator.readCatalog(2);
TreeItem<Catalog> treeItem = new MyTreeItem(root);
catalogTreeView.setRoot(treeItem);
catalogTreeView.setCellFactory(new Callback<TreeView<Catalog>, TreeCell<Catalog>>() {
@Override
public TreeCell<Catalog> call(TreeView<Catalog> param) {
return new TreeCell<Catalog>() {
@Override
protected void updateItem(Catalog catalog, boolean empty) {
super.updateItem(catalog, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(catalog.getName());
if (catalog.isDirectory()) {
setGraphic(UIResources.getDirectoryIcon());
} else if (catalog.isExecutable()) {
setGraphic(UIResources.getProgramIcon());
} else {
setGraphic(UIResources.getFileIcon());
}
}
}
};
}
});
// catalogTreeView.setCellFactory((TreeView<Catalog> p)->new MyTreeCell());
catalogTreeView.refresh();
}
3.2.2建立目录结构
根目录下包含子目录与文件,子目录即为文件夹,文件包含两种格式,一个是文本格式,一个是可执行文件,内容可以进行扩充,也可以进行缩短。如下图3-4所示:
图3-4 目录结构2
/**
* 建立目录/文件
*
* @param filePath 文件路径名
* @param property 文件属性 8为文件夹 4为文件
*/
public void create(String filePath, int property) throws Exception {
int newFilePos = firstFreeBlock();
if (newFilePos == -1) {
throw new Exception("硬盘空间不足");
}
SplitFilePath splitFilePath = splitPathAndFileName(filePath);
int parentCatalogBlockPos = getCatalogBlock(splitFilePath.getPath(), 2);//找到该文件父目录所在磁盘块
Catalog parentDir = readCatalog(parentCatalogBlockPos);
//查找该文件夹下是否有同名目录
if (existsFile(splitFilePath.getFileName(), parentDir.getStartBlock())) {
throw new Exception("已经存在同名目录,请先删除");
}
//将该文件夹的起始磁盘块设置为新文件的磁盘块
if (parentDir.getStartBlock() == -1) {
parentDir.setStartBlock(newFilePos);
writeCatalog(parentDir);
}
//将该文件夹的最后一个文件的磁盘块设置为新文件的磁盘块位置
else {
int last = getLastBlock(parentDir.getStartBlock());
disk.seek(last);
disk.writeByte(newFilePos);
}
Catalog newFile = new Catalog(splitFilePath.getFileName(), property);
newFile.setCatalogBlock(newFilePos);
setNextBlock(newFilePos, -1);//修改文件分配表
writeCatalog(newFile);//将目录项写入磁盘
System.out.println("建立文件成功");
mainController.addTreeItem(parentDir, newFile);
}
3.2.3创建目录
使用命令mkdir+文件路径/文件名进行创建,如下图3-5所示:
图3-5创建目录
/**
* 建立目录
*
* @param dirPath 目录路径
*/
public void mkdir(String dirPath) throws Exception {
SplitFilePath splitFilePath = splitPathAndFileName(dirPath);
int parentCatalogBlockPos = getCatalogBlock(splitFilePath.getPath(), 2);//找到该文件父目录所在磁盘块
Catalog parentDir = readCatalog(parentCatalogBlockPos);
//查找该文件夹下目录是否超过上限
if (countFile(parentDir.getStartBlock())) {
throw new Exception("当前目录下文件数量超过上限");
}
create(dirPath, 8);
}
/**
* 建立目录/文件
*
* @param filePath 文件路径名
* @param property 文件属性 8为文件夹 4为文件
*/
public void create(String filePath, int property) throws Exception {
int newFilePos = firstFreeBlock();
if (newFilePos == -1) {
throw new Exception("硬盘空间不足");
}
SplitFilePath splitFilePath = splitPathAndFileName(filePath);
int parentCatalogBlockPos = getCatalogBlock(splitFilePath.getPath(), 2);//找到该文件父目录所在磁盘块
Catalog parentDir = readCatalog(parentCatalogBlockPos);
//查找该文件夹下是否有同名目录
if (existsFile(splitFilePath.getFileName(), parentDir.getStartBlock())) {
throw new Exception("已经存在同名目录,请先删除");
}
//将该文件夹的起始磁盘块设置为新文件的磁盘块
if (parentDir.getStartBlock() == -1) {
parentDir.setStartBlock(newFilePos);
writeCatalog(parentDir);
}
//将该文件夹的最后一个文件的磁盘块设置为新文件的磁盘块位置
else {
int last = getLastBlock(parentDir.getStartBlock());
disk.seek(last);
disk.writeByte(newFilePos);
}
Catalog newFile = new Catalog(splitFilePath.getFileName(), property);
newFile.setCatalogBlock(newFilePos);
setNextBlock(newFilePos, -1);//修改文件分配表
writeCatalog(newFile);//将目录项写入磁盘
System.out.println("建立文件成功");
mainController.addTreeItem(parentDir, newFile);
}
3.2.4复制并粘贴目录
首先root根目录下有aaa和bbb两个目录,对目录进行复制时,需要定义两个文件类,openedFile1和openedFile2。前者存放被复制文件的绝对路径、文件的类型、文件的内容。后者存放复制的路径以及文件类型。最后调用create和write方法进行写入实现粘贴。如下图3-6所示:
图3-6复制并粘贴目录
/**
* @Description: 复制并粘贴目录
* @param [srcFilePath, desFilePath]
* @return: void
* @Author: wyq
*/
public void copy(String srcFilePath, String desFilePath) throws Exception {
OpenedFile openedFile1 = null, openedFile2 = null;
try {
openedFile1 = open(srcFilePath, OpenedFile.OP_TYPE_READ);
create(desFilePath, openedFile1.getCatalog().getProperty());
openedFile2 = open(desFilePath, OpenedFile.OP_TYPE_WRITE);
byte[] content = read(openedFile1, -1);
write(openedFile2, content, content.length);
} catch (Exception e) {
throw e;
} finally {
if (openedFile1 != null) {
close(openedFile1);
}
if (openedFile2 != null) {
close(openedFile2);
}
}
}
3.2.5删除目录
删除目录时通过递归调用将该目录下的子目录以及文件、文件的内容等进行删除。同时需要回收磁盘块,将变动情况通过磁盘使用情况展示图表现出来。bbb目录下拥有3个目录,对bbb目录进行删除操作,如下图3-7所示:
图3-7删除目录
/**
* 删除目录内部实现
*
* @param parent 要删除目录的父目录
* @param catalog 要删除的目录
*/
private void rmdir(Catalog parent, Catalog catalog) throws Exception {
//如果是文件或空文件夹,则直接删除
if (!catalog.isDirectory() || catalog.isBlank()) {
delete(parent, catalog);
return;
}
//先删除所有子目录
for (Catalog c : catalog.list()) {
rmdir(catalog, c);
}
catalog.setBlank(true);
//再删除本目录
rmdir(parent, catalog);
}
/**
* TODO 删除目录
*
* @param dirPath 目录路径
*/
public void rmdir(String dirPath) throws Exception {
SplitFilePath splitFilePath = splitPathAndFileName(dirPath);
int parentBlock = getCatalogBlock(splitFilePath.getPath(), 2);
Catalog parent = readCatalog(parentBlock);
int catalogBlock = getCatalogBlock(dirPath, 2);
Catalog catalog = readCatalog(catalogBlock);
rmdir(parent, catalog);
mainController.removeTreeItem(catalog);
}
3.2.6格式化
对磁盘进行格式化操作,即意味着回复到最开始的样子,除了系统占用的三个磁盘块之外,其他磁盘块都需要进行回收,同时对disk.txt文件进行修改,如下图3-8所示:
图3-8磁盘格式化
/**
* 格式化硬盘
*
* @throws Exception
*/
public void format() throws Exception {
Catalog root = readCatalog(2);
for (Catalog catalog : root.list()) {
if (!catalog.isDirectory()) {
delete(root, catalog);
} else {
rmdir(root, catalog);
}
}
}
public void delete(Catalog parent, Catalog c) throws Exception {
for (OpenedFile openedFile : openedFiles) {
if (openedFile.getCatalog().equals(c)) {
throw new Exception("该文件已经打开,无法删除!");
}
}
if (parent.getStartBlock() == c.getCatalogBlock()) {
parent.setStartBlock(getNextBlock(c.getCatalogBlock()));
writeCatalog(parent);
} else {
int nextBlock = parent.getStartBlock();
int pre = nextBlock;
while (nextBlock != c.getCatalogBlock()) {
pre = nextBlock;
nextBlock = getNextBlock(pre);
}
setNextBlock(pre, getNextBlock(c.getCatalogBlock()));
}
//删除目录项
setNextBlock(c.getCatalogBlock(), 0);
System.out.println("删除文件" + c.getName() + "成功");
}
private void rmdir(Catalog parent, Catalog catalog) throws Exception {
//如果是文件或空文件夹,则直接删除
if (!catalog.isDirectory() || catalog.isBlank()) {
delete(parent, catalog);
return;
}
//先删除所有子目录
for (Catalog c : catalog.list()) {
rmdir(catalog, c);
}
catalog.setBlank(true);
//再删除本目录
rmdir(parent, catalog);
}
3.3文件控制块
在每次创建文件(目录)时为每一个文件(目录)建立一个文件控制块FCB,并存储在相应的磁盘块中。每一个FCB存储文件名/目录名、文件属性、文件内容起始盘块号和文件长度,共占8个字节。
/**
*目录/文件登记项
*/
public class Catalog {
//目录项占用空间
private byte[] bytes;
//文件名
private String name;
// //文件类型,如果是目录则为空格
// private String type;
//文件属性
private int property;
//文件内容起始盘号
private int startBlock;
//文件长度,单位为盘块,如果是目录则为0
private int fileLength;
/*方便操作的附加属性*/
//是否是可执行文件
public boolean executable;
//是否是目录
private boolean isDirectory;
//目录所在磁盘块号
private int catalogBlock;
//是否为空
private boolean isBlank;
//以下为Get,Set方法在此省略
。
。
。
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + startBlock;
return result;
}
}
3.4文件管理
本系统提供命令接口,包含有关于文件的一系列操作,如文件的新建、删除、复制、粘贴、显示等多种操作。如下图3-9所示:
图3-9 文件管理
try {
if (instruction[0].equals("create")) {
OS.fileOperator.create(instruction[1], 4);
cmdView.appendText("-> 创建文件成功\n");
} else if (instruction[0].equals("delete")) {
OS.fileOperator.delete(instruction[1]);
cmdView.appendText("-> 删除文件成功\n");
} else if (instruction[0].equals("type")) {
String content = OS.fileOperator.type(instruction[1]);
cmdView.appendText(content + "\n");
} else if (instruction[0].equals("mkdir")) {
OS.fileOperator.mkdir(instruction[1]);
cmdView.appendText("-> 创建目录成功\n");
} else if (instruction[0].equals("rmdir")) {
OS.fileOperator.rmdir(instruction[1]);
cmdView.appendText("-> 删除目录成功\n");
} else if (instruction[0].equals("change") && instruction.length == 3) {
int newProperty = Integer.valueOf(instruction[2]).intValue();
OS.fileOperator.changeProperty(instruction[1], newProperty);
cmdView.appendText("-> 修改文件属性成功\n");
} else if (instruction[0].equals("run")) {
OS.fileOperator.run(instruction[1]);
cmdView.appendText("-> 运行文件成功\n");
}else if (instruction[0].equals("open")){
OpenedFile openedFile= OS.fileOperator.open(instruction[1],OpenedFile.OP_TYPE_READ_WRITE);
String content=new String(OS.fileOperator.read(openedFile,-1));
FXMLLoader fxmlLoader=new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/notepad.fxml"));
Parent parent= fxmlLoader.load();
NotepadController notepadController=fxmlLoader.getController();
notepadController.setOpenedFile(openedFile);
notepadController.setText(content);
Stage notePadStage=new Stage();
notePadStage.setScene(new Scene(parent,600,400));
notePadStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
try {
notepadController.closeFile();
} catch (Exception e) {
e.printStackTrace();
}
}
});
notePadStage.show();
}
else if (instruction[0].equals("copy")){
OS.fileOperator.copy(instruction[1],instruction[2]);
cmdView.appendText("-> 复制文件成功\n");
}
else if (instruction[0].equals("format")){
OS.fileOperator.format();
cmdView.appendText("-> 格式化硬盘成功\n");
}
else {
cmdView.appendText("-> 指令不存在\n");
return;
}
} catch (Exception ex) {
ex.printStackTrace();
String[] exception = ex.toString().split(":");
cmdView.appendText("-> " + exception[exception.length - 1].trim() + "\n");
}
3.4.1新建文件
新建文件时的同时需要建立文件的FCB,为文件分配磁盘块。当用户新建文件时,在命令行输入create 绝对路径,默认读写属性,其中,文件名命名限制是不超过3个字符的英文字符,文件内容最大长度为255。当用户输入命令敲击回车键后,系统将根据文件内容长度计算文件所需的磁盘块数,查找FAT中的空盘块,分配文件磁盘块。根据文件的路径判断文件路径是否正确,是否有同名文件。在文件路径正确等条件均满足时完成文件的建立。创建文件的FCB,更改FAT表项,将文件内容写入分配的磁盘块中。如下图3-10所示:
图3-10新建文件
/**
* 建立目录/文件
*
* @param filePath 文件路径名
* @param property 文件属性 8为文件夹 4为文件
*/
public void create(String filePath, int property) throws Exception {
int newFilePos = firstFreeBlock();
if (newFilePos == -1) {
throw new Exception("硬盘空间不足");
}
SplitFilePath splitFilePath = splitPathAndFileName(filePath);
int parentCatalogBlockPos = getCatalogBlock(splitFilePath.getPath(), 2);//找到该文件父目录所在磁盘块
Catalog parentDir = readCatalog(parentCatalogBlockPos);
//查找该文件夹下是否有同名目录
if (existsFile(splitFilePath.getFileName(), parentDir.getStartBlock())) {
throw new Exception("已经存在同名目录,请先删除");
}
//将该文件夹的起始磁盘块设置为新文件的磁盘块
if (parentDir.getStartBlock() == -1) {
parentDir.setStartBlock(newFilePos);
writeCatalog(parentDir);
}
//将该文件夹的最后一个文件的磁盘块设置为新文件的磁盘块位置
else {
int last = getLastBlock(parentDir.getStartBlock());
disk.seek(last);
disk.writeByte(newFilePos);
}
Catalog newFile = new Catalog(splitFilePath.getFileName(), property);
newFile.setCatalogBlock(newFilePos);
setNextBlock(newFilePos, -1);//修改文件分配表
writeCatalog(newFile);//将目录项写入磁盘
System.out.println("建立文件成功");
mainController.addTreeItem(parentDir, newFile);
}
3.4.2复制并粘贴文件
首先root根目录下有aaa和bbb两个目录,在aaa目录下拥有ccc文件。ccc文件中的文本内容为一行a字母。对文件进行复制时,需要定义两个文件类,openedFile1和openedFile2。前者存放被复制文件的绝对路径、文件的类型、文件的内容。后者存放复制的路径以及文件类型。最后调用create和write方法进行写入实现粘贴。如下图3-11所示:
图3-11复制并粘贴文件
/**
* @Description: 复制并粘贴文件
* @param [srcFilePath, desFilePath]
* @return: void
* @Author: wyq
*/
public void copy(String srcFilePath, String desFilePath) throws Exception {
OpenedFile openedFile1 = null, openedFile2 = null;
try {
openedFile1 = open(srcFilePath, OpenedFile.OP_TYPE_READ);
create(desFilePath, openedFile1.getCatalog().getProperty());
openedFile2 = open(desFilePath, OpenedFile.OP_TYPE_WRITE);
byte[] content = read(openedFile1, -1);
write(openedFile2, content, content.length);
} catch (Exception e) {
throw e;
} finally {
if (openedFile1 != null) {
close(openedFile1);
}
if (openedFile2 != null) {
close(openedFile2);
}
}
}
/**
* 建立目录/文件
*
* @param filePath 文件路径名
* @param property 文件属性 8为文件夹 4为文件
*/
public void create(String filePath, int property) throws Exception {
int newFilePos = firstFreeBlock();
if (newFilePos == -1) {
throw new Exception("硬盘空间不足");
}
SplitFilePath splitFilePath = splitPathAndFileName(filePath);
int parentCatalogBlockPos = getCatalogBlock(splitFilePath.getPath(), 2);//找到该文件父目录所在磁盘块
Catalog parentDir = readCatalog(parentCatalogBlockPos);
//查找该文件夹下是否有同名目录
if (existsFile(splitFilePath.getFileName(), parentDir.getStartBlock())) {
throw new Exception("已经存在同名目录,请先删除");
}
//将该文件夹的起始磁盘块设置为新文件的磁盘块
if (parentDir.getStartBlock() == -1) {
parentDir.setStartBlock(newFilePos);
writeCatalog(parentDir);
}
//将该文件夹的最后一个文件的磁盘块设置为新文件的磁盘块位置
else {
int last = getLastBlock(parentDir.getStartBlock());
disk.seek(last);
disk.writeByte(newFilePos);
}
Catalog newFile = new Catalog(splitFilePath.getFileName(), property);
newFile.setCatalogBlock(newFilePos);
setNextBlock(newFilePos, -1);//修改文件分配表
writeCatalog(newFile);//将目录项写入磁盘
System.out.println("建立文件成功");
mainController.addTreeItem(parentDir, newFile);
}
3.4.3显示文件内容
通过命令open 文件路径打开文件,调用事先渲染好的xml界面,再根据绝对路径在dist.txt文件中搜索到该文件的内容并映射到xml界面。在xml界面设置保存按键。下面为bbb文件的展开内容,如下图3-12所示:
图3-12 显示文件内容
else if (instruction[0].equals("open")){
OpenedFile openedFile= OS.fileOperator.open(instruction[1],OpenedFile.OP_TYPE_READ_WRITE);
String content=new String(OS.fileOperator.read(openedFile,-1));
FXMLLoader fxmlLoader=new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/notepad.fxml"));
Parent parent= fxmlLoader.load();
NotepadController notepadController=fxmlLoader.getController();
notepadController.setOpenedFile(openedFile);
notepadController.setText(content);
Stage notePadStage=new Stage();
notePadStage.setScene(new Scene(parent,600,400));
notePadStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
try {
notepadController.closeFile();
} catch (Exception e) {
e.printStackTrace();
}
}
});
notePadStage.show();
}
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="myos.controller.NotepadController">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="saveMenuItem" mnemonicParsing="false" onAction="#saveFile" text="save" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<TextArea fx:id="content" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
3.4.4修改文件属性
通过命令change+文件路径+代数修改文件属性。0为可读,1为可写,2为可读可写,3为可执行,4为文件,8为文件夹。下面对bbb文件进行转换文件夹操作,如下图3-13所示:
图3-13 修改文件属性
/**
* 改变文件属性
*
* @param filePath
* @param newProperty
*/
public void changeProperty(String filePath, int newProperty) throws Exception {
int catalogBlock = getCatalogBlock(filePath, 2);
Catalog catalog = readCatalog(catalogBlock);
catalog.setProperty(newProperty);
writeCatalog(catalog);
System.out.println("修改文件属性成功");
mainController.updateTreeItem(catalog);
}
/**
*已打开文件
*/
public class OpenedFile {
public static final int OP_TYPE_READ=0;//读方式打开文件
public static final int OP_TYPE_WRITE=1;//写方式打开文件
public static final int OP_TYPE_READ_WRITE=2;//读写模式
public static final int OP_TYPE_RUN=3;//执行模式
//文件路径名
private String filePath;
//文件目录项
private Catalog catalog;
//操作类型
private int opType;
//读指针
private Pointer readPointer;
//写指针
private Pointer writePointer;
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public int getOpType() {
return opType;
}
public void setOpType(int opType) {
this.opType = opType;
}
public Pointer getReadPointer() {
return readPointer;
}
public void setReadPointer(Pointer readPointer) {
this.readPointer = readPointer;
}
public Pointer getWritePointer() {
return writePointer;
}
public void setWritePointer(Pointer writePointer) {
this.writePointer = writePointer;
}
public Catalog getCatalog() {
return catalog;
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
}
}
class Pointer{
//块号
private int blockNo;
//块内地址
private int address;
public int getBlockNo() {
return blockNo;
}
public void setBlockNo(int blockNo) {
this.blockNo = blockNo;
}
public int getAddress() {
return address;
}
public void setAddress(int address) {
this.address = address;
}
}
3.4.5编辑文件
通过命令open+文件路径编辑文件内容。首先判断文件路径操作类型是否正确,否则磁盘不允许编辑。接着查找文件的FCB,得到文件的相关信息,在编辑窗口显示文件内容删除原文件的FCB信息,根据文件内容查找空白磁盘块,修改FAT表项,将文件的新FCB信息写入磁盘块,将文件内容写入磁盘块中。下面对bbb文件进行扩充与缩减的编辑,如下图3-14所示:
图3-14 编辑文件
else if (instruction[0].equals("open")){
OpenedFile openedFile= OS.fileOperator.open(instruction[1],OpenedFile.OP_TYPE_READ_WRITE);
String content=new String(OS.fileOperator.read(openedFile,-1));
FXMLLoader fxmlLoader=new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/notepad.fxml"));
Parent parent= fxmlLoader.load();
NotepadController notepadController=fxmlLoader.getController();
notepadController.setOpenedFile(openedFile);
notepadController.setText(content);
Stage notePadStage=new Stage();
notePadStage.setScene(new Scene(parent,600,400));
notePadStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
try {
notepadController.closeFile();
} catch (Exception e) {
e.printStackTrace();
}
}
});
notePadStage.show();
}
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="myos.controller.NotepadController">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="saveMenuItem" mnemonicParsing="false" onAction="#saveFile" text="save" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
<center>
<TextArea fx:id="content" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
3.4.6删除文件
删除文件时首先判断该文件是否被打开,若打开则停止删除并抛出异常。再根据绝对路径找到所在的磁盘块,然后通过while循环将分配表中的该文件下内容全部删除,同时回收磁盘块。将变动情况通过磁盘使用情况展示图表现出来,如下图3-15所示:
图3-15 删除文件
/**
* 删除目录内部实现
*
* @param parent 要删除目录的父目录
* @param catalog 要删除的目录
*/
private void rmdir(Catalog parent, Catalog catalog) throws Exception {
//如果是文件或空文件夹,则直接删除
if (!catalog.isDirectory() || catalog.isBlank()) {
delete(parent, catalog);
return;
}
//先删除所有子目录
for (Catalog c : catalog.list()) {
rmdir(catalog, c);
}
catalog.setBlank(true);
//再删除本目录
rmdir(parent, catalog);
}
/**
* TODO 删除目录
*
* @param dirPath 目录路径
*/
public void rmdir(String dirPath) throws Exception {
SplitFilePath splitFilePath = splitPathAndFileName(dirPath);
int parentBlock = getCatalogBlock(splitFilePath.getPath(), 2);
Catalog parent = readCatalog(parentBlock);
int catalogBlock = getCatalogBlock(dirPath, 2);
Catalog catalog = readCatalog(catalogBlock);
rmdir(parent, catalog);
mainController.removeTreeItem(catalog);
3.5容错与提示
当一个目录下文件个数超过8个时,将无法创建;当文件或者目录复制后,在其同源文件夹下进行粘贴,程序会进行阻止,并抛出异常;当打开一个不存在的文件时,程序会进行阻止,应重新输入。
图3-16 一个根目录下只允许8个子目录
图3-17 目标文件夹是源文件夹的子文件夹提示
图3-18 文件不存在
四、源代码
CSDN下载地址:河北大学2019级操作系统课程设计-其它文档类资源-CSDN下载