文件管理
一、实验目的:
1.了解os中文件的组织和管理。
2.熟悉文件中所用的数据结构。
二、实验要求:
1. 设计一个N个用户的文件系统,每个用户最多保存m个文件。
2. 限制用户在一次使用中只能打开L个文件。
3. 系统应能检查输入命令的正确性,出错能显示出原因。
4. 对文件必须设置保护措施,如只读、写、执行等。在每次打开时再次设置保护级别,即可有二级保护。
5. 对文件的操作至少有以下几条命令
create:建文件
delete:删文件
open:打开文件
close:关闭文件
read:读文件
write:写文件
举例:主文件目录
mfd=record
username :string[maxlen];
files :array[1..L] of ufd;
ofiles :arrau[1..S] of uod
end;
用户打开文件目录表:
uod=record
filename:string[maxlen];
attrib:attrib;
len:integer;
status:(open,create);
rp,up:integer;
end;
用户文件目录:
ufd=record
fielname:string[maxlen];
attrib?ro,rw);
len:integer;
addr:integer;
end;
流程图:
二、主要数据结构
界面采用VC6 MFC环境开发
#define MAXFILE 20 //每个用户最多保存20个文件
#define MAXUSER 10 //假想文件系统最多支持的人数
#define BLOCKSIZE 32 //虚拟磁盘中物理块为每块32字节
#define DISKSIZE BLOCKSIZE*1000 //虚拟磁盘容量为1000*32=32K
struct UFD //说明文件项的结构数组
{
char FileName[15];
char Time[16]; //文件建立或修改时间 如2003/5/6 12:00
bool IsExist; //文件是否存在,删除时标为0
bool IsShared; //共享标记,共享文件可被其它用户所访问
bool AttrRead; //文件是否可读
bool AttrWrite; //文件是否可写
bool AttrExecute; //文件是否可执行
HTREEITEM treeNode; //用于树控件显示的结点句柄
USHORT FileLen; //文件占用字节数
USHORT BlockNum; //文件占用的物理块数
USHORT FileLink[100];//文件物理块地址数组,每块32字节,限定一个文件最大100*32=3200字节
};
struct MFD
{
char UserName[10]; //主目录用户名
bool IsExist; //该用户否存在
UFD ufd[MAXFILE]; //用户文件数组
USHORT nItem;//UFD个数
};
struct HEADBLOCK
{
BYTE pStack; //堆栈指针
SHORT pBlock[10]; //块号 pBlock[10]是下一个盘块号逻辑地址
};
struct BLOCK //虚拟磁盘的物理块数据结构
{
union{
BYTE block[32]; //一块为32字节
HEADBLOCK HeadInfo;
};
};
struct FAT
{
BLOCK SuperBlock; //超级块,指示第一个空闲块逻辑号
USHORT MaxOpen; //该用户同时可打开的最大文件数
USHORT UserNum; //最户数
MFD Mfd[MAXUSER]; //最多可支持10个用户
};
//空闲块成组链接法
bool OpenList[MAXUSER][MAXFILE]; //描述文件是否打开的布尔型数组
FAT FileFAT; //描述文件记录项的FAT结构
CFile FATIO; //负责和VDISK.DAT打交道的文件句柄
CString CurrentUser; //当前登录的用户名
Int CurrentID; //前前登录的用户标识号
说明:本实验采用模拟文件结构的方法,把记录用户帐号,用户文件和磁盘块的信息用当前目录下的VDISK.DAT来记录,可以把VDISK.DAT看成是一个虚拟的磁盘,其头部是FAT结构,用来记录各个用户和文件信息,紧接着是空闲块成组链接法的数据结构,每块32字节,每组10块,共1000块,也就是说,用户文件数据的总容量是32*1000字节,如果程序当前目录下找不到用于做实验用的VDISK.DAT,在登录时程序会提示是否“格式化虚拟磁盘”也就是新建一个VDISK.DAT文件,接着,程序会显示“用户管理”的窗口,此时应新建几个帐号用于登录做实验。
登录后,程序会显示该用户的所有文件,右方的文件列表会显示每个文件的属性信息,和WINDOWS的“资源管理器”相似。用鼠标双击列表的每个文件就可以查看文件的内容。单击“新建文件”按钮可以创建一个新的用户文件。
选中某个文件后,就可以进行相应的操作,如“修改文件”、“删除文件”或是“打开文件”,删除某个文件时检查该文件的属性,如果是只读的,就是显示警告窗口让用户确认是否一定要删除。
硬盘工具可以用图形方式显示“磁盘”块的详细情况,空白块说明该块没有使用,暗红色的说明块已分配,窗口还统计占用的空间大小、用户数等信息。
程序中用到的结构图如下图所示:
MFD
三、主要算法与部分代码
void CDlgUser::OnAddUser()
{
UpdateData(true);
if (FileFAT.UserNum>=10)
{
AfxMessageBox("用户数量已达到最大10个,你可以删除一些无用的帐号!");
return;
}
for(int i=0;i<10;i++)
{
if(FileFAT.Mfd[i].IsExist==false)
{
wsprintf(FileFAT.Mfd[i].UserName,m_UserName);
FileFAT.Mfd[i].IsExist=true;
FileFAT.Mfd[i].nItem=0;
break;
}
}
FileFAT.UserNum++;
WriteFAT();
ReadFAT();
ShowUser();//显示现有的用户列表
m_UserName.Empty();
UpdateData(false);
}
bool ReadBlock(int id, BLOCK* buffer) //读指定块号内容(32字节)到buffer所指向的单元
{
if(!FATIO) return false; //如果虚拟磁盘文件句柄为空,出错返回
if(!FATIO.Seek(sizeof(FAT)+(id-1)*BLOCKSIZE,CFile::begin)) return false;//移动文件指针到指定块号
if(FATIO.Read(buffer,BLOCKSIZE)!=BLOCKSIZE) return false;//读该块的内容到buffer指定的单元
return true;
}
bool WriteBlock(int id, BLOCK* buffer) //把buffer所指向的单元的内容(32字节)写到指定的物理块中
{
if(!FATIO) return false;
if(!FATIO.Seek(sizeof(FAT)+(id-1)*BLOCKSIZE,CFile::begin)) return false;
FATIO.Write(buffer,BLOCKSIZE);
return true;
}
USHORT AllocBlock() //分配一物理块,成功返回逻辑块号,失败返回0;
{
if(!FileFAT.SuperBlock.HeadInfo.pBlock[FileFAT.SuperBlock.HeadInfo.pStack]) return 0;
//没有空间可分配,失败返回
if(FileFAT.SuperBlock.HeadInfo.pStack<9)//如果一组的空闲块没有分配完
{
WriteFAT();
//将返回超级块所指的空闲块,再将堆栈指针加一
return FileFAT.SuperBlock.HeadInfo.pBlock[FileFAT.SuperBlock.HeadInfo.pStack++];
}
else //如果分配的空闲块是组头
{
WriteFAT();
USHORT returnBlock=FileFAT.SuperBlock.HeadInfo.pBlock[9];
//将要分配的组头复制到超级块,再返回组头作为空闲块
if(!ReadBlock(returnBlock,&FileFAT.SuperBlock)) return 0;
return returnBlock;
}
WriteFAT();
return 0;
}
bool FreeBlock(USHORT BlockID) //回收一物理块,成功返回TRUE,失败返回FALSE
{
if(FileFAT.SuperBlock.HeadInfo.pStack>0)//如果该组的空闲没有回收满
{
//堆栈指针退一,把回收的块号记入超级块
FileFAT.SuperBlock.HeadInfo.pBlock[--FileFAT.SuperBlock.HeadInfo.pStack]=BlockID;
BLOCK newBlock;
//把回收的块内容清空
if (!WriteBlock(BlockID,&newBlock)) return false;
}
else//如果该组已回收满,需要加入一新组的话
{
if (!WriteBlock(BlockID,&FileFAT.SuperBlock)) return false;//将超级块的栈内容复制到要回收的块中
FileFAT.SuperBlock.HeadInfo.pStack=9;//将超级块的栈指针指向回收的块
FileFAT.SuperBlock.HeadInfo.pBlock[9]=BlockID;
}
WriteFAT();
return true;
}
bool ReadFAT() //从虚拟磁盘中读取FAT结构信息到FileFAT中
{
ZeroMemory(&FileFAT,sizeof(FileFAT));
FATIO.SeekToBegin();
if (!FATIO.Read(&FileFAT,sizeof(FileFAT))) return false;
return true;
}
bool Format() //格式化虚拟磁盘,创建VDISK.DAT,所有用户和文件信息将被清空!
{
CString FATFile;
int i;
char fname[128];
GetCurrentDirectory(128,fname); //FAT表信息保存在当前目录的VDISK.DAT中
FATFile.Format("%s",fname);
if (FATFile.Right(1)!="\\")
FATFile+="\\VDISK.DAT";
else
FATFile+="VDISK.DAT";
CFile fout;
if( !fout.Open(FATFile,CFile::modeCreate|CFile::modeWrite,NULL))
return false;
ZeroMemory(&FileFAT,sizeof(FileFAT));
FileFAT.SuperBlock.HeadInfo.pStack=0;
for(i=0;i<10;i++)
{
FileFAT.SuperBlock.HeadInfo.pBlock[i]=i+1;
}
fout.WriteHuge(&FileFAT,sizeof(FileFAT));
BYTE *Buffer;
Buffer=(BYTE*)malloc(DISKSIZE);//申请成组链接法所需的磁盘块空间
ZeroMemory(Buffer,DISKSIZE);
BLOCK newBlock;
for (i=10;i<=990;i+=10)
{
ZeroMemory(&newBlock,sizeof(newBlock));
newBlock.HeadInfo.pStack=0;
for (int k=0;k<10;k++)
newBlock.HeadInfo.pBlock[k]=i-9+k;
memcpy(Buffer+32*(i-1),&newBlock,32);
}
fout.WriteHuge(Buffer,DISKSIZE); //将格式化的块信息写入VDISK.DAT
free(Buffer);
fout.Close();
AfxMessageBox("创建虚拟磁盘文件成功!请选择用户管理添加新用户。");
return true;
}
//StartIO()用于完成与虚拟磁盘的IO的初始化工作
void StartIO() //打开VDISK.DAT文件,把该文件句柄保存在全局变量中,如果该文件不存在,创建并初始化该文件。
{
CString FATFile;
char fname[128];
GetCurrentDirectory(128,fname); //FAT表信息保存在当前目录的VDISK.DAT中
FATFile.Format("%s",fname);
if (FATFile.Right(1)!="\\")
FATFile+="\\VDISK.DAT";
else
FATFile+="VDISK.DAT";
bFormated=true;
if (!FileExist(FATFile))
{
AfxMessageBox("当前目录下找不到VDISK.DAT,该文件是用来模拟磁盘及文件管理表。\n单击确定创建新的虚拟磁盘文件!");
bFormated=false;
if (!Format())
{
AfxMessageBox("创建文件系统出错!");
return;
}
}
if(!FATIO.Open(FATFile,CFile::modeReadWrite|CFile::shareDenyWrite,NULL))
{
AfxMessageBox("调入FAT表失败,文件系统可能出错!");
bFormated=false;
return;
}
}
bool CreateNewFile(CString FileName, CString FileContent, bool AttrRead, bool AttrWrite, bool AttrExecute, bool AttrShare) //创建一个新文件
{
if (FileNameExist(FileName)) return false; //如果要建立的文件与现有文件重名,失败返回
int i,FilePos,FileLen,BlockNum,BlockID;
bool flag=false;
FileContent.TrimLeft();FileContent.TrimRight();
FileLen=FileContent.GetLength();
char Content[32];//把文件内容分割成每个物理块大小后送content字符数组,以备写入磁盘块中
for(i=0;i
{
if(!FileFAT.Mfd[CurrentID].ufd[i].IsExist)//扫描所有文件项,如果标记不存在就准备写入新建文件信息
{
flag=true;FilePos=i;
break;
}
}
if(!flag) return false;
BlockNum=(int)FileLen/BLOCKSIZE+1;//计算文件占用的块数
for(i=0;i
{
BlockID=AllocBlock();//得到分配的块的逻辑号
if(BlockID)
{
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLink[i]=BlockID;//逐个将块号记入文件项中
wsprintf(Content,FileContent.Mid(i*32,32));//写入一个块的内容到缓冲区
if(!WriteBlock(BlockID,(BLOCK*)Content)) return false;//从缓冲区写到虚拟磁盘
}
else
{
return false;
}
}
//以下是填写文件的属性,时间等信息
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].FileName,FileName.Left(15));
FileFAT.Mfd[CurrentID].ufd[FilePos].IsExist=true;
FileFAT.Mfd[CurrentID].ufd[FilePos].BlockNum=BlockNum;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrExecute=AttrExecute;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrRead=AttrRead;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrWrite=AttrWrite;
FileFAT.Mfd[CurrentID].ufd[FilePos].IsShared=AttrShare;
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLen=FileContent.GetLength();
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].Time,GetCurrentTime());
FileFAT.Mfd[CurrentID].nItem++;
WriteFAT();
ShowUserFiles();//刷新用户文件列表,以便将新建的文件显示出来
return true;
}
CString ReadFile(CString FileName) //给定当前用户的文件名,将文件内容以Cstring字符串返回
{
int i,FilePos;
bool flag=false;
char Buffer[32];
CString strReturn;
for(i=0;i
{
if(FileFAT.Mfd[CurrentID].ufd[i].FileName==FileName&&FileFAT.Mfd[CurrentID].ufd[i].IsExist==true)
{
flag=true;FilePos=i;break;
}
}
if(!flag) return "";
for(i=0;i
{
ZeroMemory(Buffer,32);
if(!ReadBlock(FileFAT.Mfd[CurrentID].ufd[FilePos].FileLink[i],(BLOCK*)Buffer)) return "";
strReturn+=Buffer;
}
return strReturn;
}
bool KillFile(CString FileName)//根据文件名删除用户当前文件
{
int i,FilePos;
bool flag=false;
for(i=0;i
{
if(FileFAT.Mfd[CurrentID].ufd[i].FileName==FileName&&FileFAT.Mfd[CurrentID].ufd[i].IsExist==true)
{
flag=true; FilePos=i; break;
}
}
if(!flag) return false;
for(i=0;i
if(!FreeBlock(FileFAT.Mfd[CurrentID].ufd[FilePos].FileLink[i])) return false;
FileFAT.Mfd[CurrentID].nItem--;
ZeroMemory(&FileFAT.Mfd[CurrentID].ufd[FilePos],sizeof(UFD));
WriteFAT();
return true;
}
bool ModifyFile(CString FileName, CString FileContent, bool AttrRead, bool AttrWrite, bool AttrExecute, bool AttrShare) //修改文件,包括文件名、属性和内容
{
int i,FilePos,BlockNum,BlockID;
bool flag=false;
FileContent.TrimLeft();
FileContent.TrimRight();
char Content[32];
for(i=0;i
{
if(FileFAT.Mfd[CurrentID].ufd[i].FileName==SelectedFileName)
{
flag=true; FilePos=i;break;
}
}
if(!flag) return false;
for(i=0;i
{
if(!FreeBlock(FileFAT.Mfd[CurrentID].ufd[FilePos].FileLink[i])) return false;
}
ZeroMemory(&FileFAT.Mfd[CurrentID].ufd[FilePos].BlockNum,200);
BlockNum=(int)FileContent.GetLength()/BLOCKSIZE+1;
for(i=0;i
{
BlockID=AllocBlock();
if(BlockID)
{
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLink[i]=BlockID;
wsprintf(Content,FileContent.Mid(i*32,32));
if(!WriteBlock(BlockID,(BLOCK*)Content)) return false;
}
else
{
return false;
}
}
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].FileName,FileName.Left(15));
FileFAT.Mfd[CurrentID].ufd[FilePos].IsExist=true;
FileFAT.Mfd[CurrentID].ufd[FilePos].BlockNum=BlockNum;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrExecute=AttrExecute;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrRead=AttrRead;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrWrite=AttrWrite;
FileFAT.Mfd[CurrentID].ufd[FilePos].IsShared=AttrShare;
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLen=FileContent.GetLength();
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].Time,GetCurrentTime());
WriteFAT();
ShowUserFiles();
return true;
}
void CDlgDisk::CalcBlock()// “磁盘工具”中的计算并显示磁盘块使用情况功能
{
BYTE DiskImage[32000];//磁盘数据区所有物理块映像数组
FATIO.Seek(sizeof(FileFAT),CFile::begin);//跳过FAT结构,定位文件指针至数据区物理块头
FATIO.Read(&DiskImage,32000);//读物理块映像
register sum=0;//保存使用的总块数
for(int i=0;i
{
m_ctrlChecker.SetBlock(FileFAT.SuperBlock.HeadInfo.pBlock[i]-1, RGB(200, 0, 0));
sum++;
}
BLOCK testBlock;
memcpy(&testBlock,&DiskImage[FileFAT.SuperBlock.HeadInfo.pBlock[9]*32],BLOCKSIZE);
while(testBlock.HeadInfo.pBlock[9]>0)
{
for(i=0;i<10;i++)
{
m_ctrlChecker.SetBlock(testBlock.HeadInfo.pBlock[i]-1, RGB(200,0,0));
sum++;
}
memcpy(&testBlock,&DiskImage[testBlock.HeadInfo.pBlock[9]*32],BLOCKSIZE);
}
AddPiece(RGB(200,0,0),(int)sum*360/100,"Used");
m_Used.Format("已用空间:%d 字节",sum*32);
m_UserNum.Format("用户数:%d",FileFAT.UserNum);
UpdateData(false);
}