一、 实验目的
了解在Windows中,文件系统如何管理保存在磁盘、光盘等存储介质上的信息。并通过文件系统提供的各种API,对文件进行同步和异步读写,深入了解Windows文件系统的功能和作用以及同步I/O和异步I/O的特点。
熟悉Linux文件系统提供的有关文件操作的系统调用。文件系统是使用计算机信息系统的重要接口。通过使用文件系统的系统调用命令操作文件,以达到对文件系统实现功能的理解和掌握。
二、 实验内容
复制文件(需要Windows版本和Linux版本)
完成一个目录复制命令mycp,包括目录下的文件和子目录, 运行结果如下:
说明:
Linux: creat,read,write等系统调用
Windows:CreateFile(), ReadFile(), WriteFile(), CloseHandle()等函数
三、 实验环境
Windows Platform:
Linux Platform:
Gedit and Terminal
四、 实验方法与步骤
1、实验方法
滤清实验思路,对于本实验无论在Windows还是Linux上首先是打开源目录,遍历源目录中的每一项,如果是文件,就在目标目录的相应位置创建文件,然后复制文件内容,如果是文件夹,在目标位置建立文件夹,然后递归进入该文件夹继续操作。这里要注意的是在建立文件夹和文件的时候,要同时把源文件的权限复制到目标文件上去。
2、实验步骤
2.1 Windows版本
(1)、创建三个函数,分别为主函数(main)、单文件复制函数(CopyFile)、目录文件复制函数(mycp);
(2)、确定调用关系,main函数调用CopyFile函数和mycp函数,mycp函数需要调用CopyFile函数进行文件的直接复制;
(3)、函数解析
①、main()函数用来接受源目录和目标目录
int main(int argc,char *argv[])
{
判断命令行输入参数的个数是否正确,若不正确进行相应的提示;
若参数个数正确,则利用FindFirstFile()函数查找源文件,如果查找源文件失败进行相应的提示;利用FindFirstFile()函数查找目标文件的路径,若返回的句柄为INVALID_HANDLE_VALUE时,为目标文件创建目录;
执行mycp操作;
执行完时进行相应的提示,并退出。
}
②、CopyFile()函数用来进行文件复制
void CopyFile(char *fsource, char *ftarget)
{
定义变量;
利用FindFirstFile()函数查找指向源文件路径的文件并将源文件的信息保存至WIN32_FIND_DATA结构体中,并返回相应的句柄;
利用CreateFile()创建两个文件,分别为源文件和目标文件;
利用WIN32_FIND_DATA结构体中的文件长度低32位减去文件长度的高32位,得到源文件的大小;
从源文件中读取数据,并将数据写入新开的缓冲区中;
将写入缓冲区中的数据写入到目标文件中;
关闭相关的句柄。
}
③、mycp()函数用于将源目录信息复制到目标目录下
void mycp(char *fsource, char *ftarget)
{
定义和初始化相关变量;
利用FindFirstFile()函数查找指向源文件路径的文件并将源文件的信息保存至WIN32_FIND_DATA结构体中,并返回相应的句柄hfind;
if(hfind != INVALID_HANDLE_VALUE)
{
while (FindNextFile(hfind,&lpfindfiledata) != 0)//循环查找FindFirstFile()函数搜索后的下一个文件
{
if(根据文件的属性得到是目录文件时)
{
初始化相关变量;
利用CreateDirecty()函数为目标文件创建目录;
递归调用mycp()函数直到最底层为止;
将执行后的操作写入到fsource和ftarget中。
}
else(不是目录文件时)
{
初始化相关变量;
CopyFile();//调用文件复制函数;
将执行后的操作写入到fsource和ftarget中。
}
}
}
else
{
查找指定文件路径的文件失败,进行相应的提示;
}
}
2.2 Linux版本
(1)、创建三个函数,分别为主函数(main)、单文件复制函数(CopyFile)、目录文件复制函数(mycp);
(2)、确定调用关系,main函数调用CopyFile函数和mycp函数,mycp函数需要调用CopyFile函数进行文件的直接复制;
(3)、函数解析
①、main()函数用来接受源目录和目标目录
int main(int argc,char *argv[])
{
判断命令行输入参数的个数是否正确,若不正确进行相应的提示;
若参数正确,则打开源文件,如果打开失败进行相应的提示;
若上述两步都正确,保存源文件的信息,并以源文件的st_mode创建新目录,修改目标目录的访问时间和修改时间;
执行mycp操作;
执行完时进行相应的提示,并退出。
}
②、CopyFile()函数用来进行文件复制
void CopyFile(char *fsource, char *ftarget)
{
以O_RDONLY的形式打开源文件,并返回文件描述符fd1,若fd1为-1表示打开失败进行相应的提示;
将源文件的信息填入struct stat的结构体当中;
以源文件的st_mode创建新文件,并返回文件描述符fd2,若fd2为-1表示创建文件失败进行相应的提示;
读取源文件并返回读出的字节数,如果返回的字节数大于0,执行下面的操作:
进行写入操作,写入时要传送的字节数为读取源文件时返回的字节数;如果写入操作的写入字节数不等于读取时读出的字节数,说明写入失败,进行相应的提示。
修改写入文件的时间属性;
关闭打开的文件。
}
③、mycp()函数用于将源目录文件复制到目标目录下
void mycp(char *fsource, char *ftarget)
{
初始化相关变量;
打开目录,返回指向DIR结构的指针;
while((entry= readdir(dir)) != NULL)//读目录
{
首先判断目录;
if (entry->d_type == 4)//读到的是目录
{
修改参数;
stat(source,&statbuf);//统计source文件属性信息并放入statbuf中
mkdir(target,statbuf.st_mode);//创建目标目录
修改目标文件的访问和修改时间;
递归调用mycp()函数直到最底层为止;
将执行后的操作写入到fsource和ftarget中。
}
else//没有子目录,直接进行复制
{
修改参数;
CopyFile();//调用文件复制函数;
将执行后的操作写入到fsource和ftarget中。
}
}
}
五、 实验结果
在Windows中的实验截图:
图中是将桌面的一个名叫“火车上的任务”这个文件夹复制为名为1的文件夹。
在Linux中的实验截图:
图中是将文件夹DM_DATA复制为名叫1的文件夹。
六、 实验分析与总结
通过熟悉Windows和Linux的文件系统相关理论,可以实现该实验并取得上面的效果。在实现的过程中有些许问题已经得到解决。先总结如下:
在Windows中:
在Windows中除过CreateFile(), ReadFile(),WriteFile(), CloseHandle()等系统调用外还需掌握三个目录操作的函数和文件属性返回值的具体情况。
(1)、CreateDirectory()函数用于创建目录
DWORD CreateDirectory(LPTSTR lpszPath,//新创建目录的路径名
LPSECURITY_ATTRIBUTES lpsa);//为目录设置的安全属性结构
返回值:成功时返回TRUE,否则返回FALSE,且通过函数GetLastError()可获得错误码。
(2)、FindFirstFile()查找指定文件路径的文件
(3)、FindNextFile()查找FindFirstFile()函数搜索后的下一个文件函数
(4)、GetFileAttributes()函数的返回值整理如下:
橙色标记的属性为Windows系统中文件的公有属性,其中“只读”、“隐藏”、“系统”、“存档”为文件的四种基本属性。compressed,content_indexed,encrypted只存在于NTFS分区中。
文件去掉全部属性后(四种基本属性),将自动标记为normal。同时具有system和hidden属性的文件会在系统中彻底隐形,这也是病毒常用的伎俩。commpressed和encrypted不能共存。默认情况下文件都有content_indexed属性。
在Linux中:
在Linux中,除过creat,read,write,open等系统调用外还需掌握两个结构体和一个函数。
structutimbuf
{//修改文件时间的结构体
time_tactime; //文件数据的最后存取时间
time_tmodtime; //文件数据的最后修改时间
}
和教材中并不完善的dirent结构体
structdirent
{
long d_ino; //inode number索引节点号
off_t d_off; //offset to this dirent在目录文件中的偏移
unsigned shortd_reclen; //length of this d_name文件名长
unsigned chard_type; //the type of d_name文件类型
chard_name[NAME_MAX+1]; //filename(null-terminated)文件名,最长255字符
}
通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中。
d_type:4表示为目录,8表示为文件;
d_reclen:16表示子目录或文件,24表示非子目录;
d_name:目录或文件的名称。
一个函数:
utime()用于更改文件的访问和修改时间
#include <utime.h>
int utime( const char *pathname, conststruct utimbuf *times );
返回值:若成功则返回0,若出错则返回-1
七、实验源代码
//********************************************
/*名称:mycp.c
/*描述:完成一个目录复制命令mycp,包括目录下的文件和子目录
/*作者:野狼
/*日期:2017-04-22
/********************************************/
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>
#include<Windowsx.h>
#include<string.h>
#define buf_size 4096
/*
查找指定文件路径的文件FindFirstFile()函数
HANDLE FindFirstFile(LPCTSTR lpFileName, //file name
LPWIN32_FIND_DATA lpFindFileData //data buffer
);
返回值:如果调用成功,返回值为非0,否则返回值为0.
*/
//直接文件复制
void CopyFile(char * fsource, char * ftarget)
{
WIN32_FIND_DATA lpfindfiledata;
//查找指定文件路径
HANDLE hfind = FindFirstFile(fsource,//文件名
&lpfindfiledata);//数据缓冲区
HANDLE hsource = CreateFile(fsource,//指向打开文件名的指针
GENERIC_READ | GENERIC_WRITE,//指定对象的访问类型(通过读和写联合访问可以实现文件的读写)
FILE_SHARE_READ,//与其他进程共享文件的方式
NULL,//指向安全属性的指针(NULL表示默认安全对象)
OPEN_ALWAYS,//指定如何创建文件(OPEN_ALWAYS为如果文件存在则打开文件,否则创建新的文件)
FILE_ATTRIBUTE_NORMAL,//指定文件的属性和标志(FILE_ATTRIBUTE_NORMAL设置为默认属性)
NULL);//用于复制文件句柄
HANDLE htarget = CreateFile(ftarget,//指向创建文件名的指针
GENERIC_READ | GENERIC_WRITE,//指定对象的访问类型(通过读和写联合访问可以实现文件的读写)
FILE_SHARE_READ,//与其他进程共享文件的方式
NULL,//指向安全属性的指针(NULL表示默认安全对象)
CREATE_ALWAYS,//指定如何创建文件(CREATE_ALWAYS为创建一个已经存在的新文件,如果存在会将现有的文件清空且重写文件)
FILE_ATTRIBUTE_NORMAL,//指定文件的属性和标志(FILE_ATTRIBUTE_NORMAL设置为默认属性)
NULL);//用于复制文件句柄
//源文件的大小
LONG size = lpfindfiledata.nFileSizeLow - lpfindfiledata.nFileSizeHigh;
DWORD wordbit;
//新开缓冲区,保存数据
int *BUFFER = new int[size];
//源文件读数据
ReadFile(hsource,//指定要读的文件句柄
BUFFER,//指向存放从文件读的数据的缓冲区的地址指针
size,//要从文件读的字节数
&wordbit,//存放实际从文件中读的字节数的变量地址
NULL);//同步方式打开设置为NULL
//目标文件写数据
WriteFile(htarget,//指定要写的文件对象的句柄
BUFFER,//指向要写入文件的数据缓冲区指针
size,//要写入文件的字节数
&wordbit,//实际写入的字节数
NULL);//同步方式打开设置为NULL
CloseHandle(hfind);
CloseHandle(hsource);
CloseHandle(htarget);
}
//将源目录信息复制到目标目录下
void mycp(char * fsource, char * ftarget)
{
WIN32_FIND_DATA lpfindfiledata;
char source[buf_size];
char target[buf_size];
lstrcpy(source, fsource);
lstrcpy(target, ftarget);
lstrcat(source, "\\*.*");
lstrcat(target, "\\");
HANDLE hfind = FindFirstFile(source,//文件名
&lpfindfiledata);//数据缓冲区
if (hfind != INVALID_HANDLE_VALUE)
{
while (FindNextFile(hfind, &lpfindfiledata) != 0)//循环查找FindFirstFile()函数搜索后的下一个文件
{
//查找下一个文件成功
if ((lpfindfiledata.dwFileAttributes) == 16)//判断是否是目录(若为目录FILE_ATTRIBUTE_DIRECTORY是16)
{
if ((strcmp(lpfindfiledata.cFileName, ".") != 0) && (strcmp(lpfindfiledata.cFileName, "..") != 0))
{
memset(source, '0', sizeof(source));
lstrcpy(source, fsource);
lstrcat(source, "\\");
lstrcat(source, lpfindfiledata.cFileName);//追加文件
lstrcat(target, lpfindfiledata.cFileName);
CreateDirectory(target,NULL);//为目标文件创建目录
mycp(source, target);//进入子目录复制
lstrcpy(source, fsource);
lstrcat(source, "\\");
lstrcpy(target, ftarget);
lstrcat(target, "\\");
}
}
else//无目录
{
memset(source, '0', sizeof(source));
lstrcpy(source, fsource);
lstrcat(source, "\\");
lstrcat(source, lpfindfiledata.cFileName);
lstrcat(target, lpfindfiledata.cFileName);
CopyFile(source, target);//直接调用文件复制函数
lstrcpy(source, fsource);
lstrcat(source, "\\");
lstrcpy(target, ftarget);
lstrcat(target, "\\");
}
}
}
else
{
printf("查找指定文件路径的文件失败!\n");
}
}
int main(int argc, char *argv[])
{
WIN32_FIND_DATA lpfindfiledata;
if (argc != 3)
{
printf("参数错误!\n");
}
else
{
if (FindFirstFile(argv[1], &lpfindfiledata) == INVALID_HANDLE_VALUE)
{
printf("查找源文件路径失败!\n");
}
if (FindFirstFile(argv[2], &lpfindfiledata) == INVALID_HANDLE_VALUE)
{
CreateDirectory(argv[2],NULL);//为目标文件创建目录
}
mycp(argv[1], argv[2]);
}
printf("复制完成!\n");
return 0;
}
/********************************************/
/*名称:mycp.c
/*描述:完成一个目录复制命令mycp,包括目录下的文件和子目录
/*作者:野狼
/*日期:2017-04-22
/********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include <utime.h>
#define buf_size 4096
//文件复制函数
void CopyFile(char *fsource, char *ftarget)
{
//打开原文件,返回文件描述符
int fd1 = open(fsource, O_RDONLY);
if (fd1 == -1)
{
printf("打开文件失败!\n");
}
//fd2用于存放目标文件的文件描述符
int fd2;
struct stat statbuf;
struct utimbuf timeby;
char BUFFER[buf_size];
int wordbit;
//将原文件的信息放入statbuf中
stat(fsource,&statbuf);
//创建新文件,返回文件描述符
fd2 = creat(ftarget,statbuf.st_mode);
if (fd2 == -1)
{
printf("创建文件失败!\n");
}
//读取源文件字节数
/*
read(write)(文件的描述符,读出(写入)文件数据的字节数组,要传送的字节个数);
返回值:读出(写入)的字节数
*/
while((wordbit = read(fd1,BUFFER,buf_size)) > 0)
{
if (write(fd2,BUFFER,wordbit) != wordbit)//写入目标文件
printf("写入失败!\n");
}
timeby.actime = statbuf.st_atime;//修改时间属性
timeby.modtime = statbuf.st_mtime;
close(fd1);
close(fd2);
}
//将源目录文件复制到目标目录下
void mycp(char *fsource,char *ftarget)
{
char source[buf_size];
char target[buf_size];
struct stat statbuf;
struct utimbuf timeby;
struct dirent *entry;
DIR *dir;
strcpy(source,fsource);
strcpy(target,ftarget);
//打开目录,返回指向DIR结构的指针
dir = opendir(source);
while((entry = readdir(dir)) != NULL)//读目录
{
if (strcmp(entry->d_name,".") == 0 || strcmp(entry->d_name,"..") == 0)
{
continue;
}
if (entry->d_type == 4)//读到的是目录
{
strcat(source,"/");
strcat(source,entry->d_name);
strcat(target,"/");
strcat(target,entry->d_name);
//统计source文件属性信息并放入statbuf中
stat(source,&statbuf);
//创建目标目录
mkdir(target,statbuf.st_mode);
//修改文件存取和修改时间
timeby.actime = statbuf.st_atime;
timeby.modtime = statbuf.st_mtime;
//修改文件的访问时间和修改时间
utime(target,&timeby);
mycp(source,target);
strcpy(source,fsource);
strcpy(target,ftarget);
}
else//没有子目录,直接复制
{
strcat(source,"/");
strcat(source,entry->d_name);
strcat(target,"/");
strcat(target,entry->d_name);
CopyFile(source,target);//调用文件复制函数
strcpy(source,fsource);
strcpy(target,ftarget);
}
}
}
int main(int argc,char *argv[])
{
struct stat statbuf; //stat结构
struct utimbuf timeby; //文件时间结构
/*
struct utimbuf
{
time_t actime; //文件数据的最后存取时间
time_t modtime; //文件数据的最后修改时间
}
*/
DIR *dir; //DIR结构的指针,指向目录的第一个文件
if (argc != 3)
{
printf("参数错误!\n");
}
else
{
/*打开目录,关闭目录*/
if ((dir = opendir(argv[1])) == NULL)
{
printf("源文件打开错误!\n");
}
if ((dir = opendir(argv[2])) == NULL)
{
stat(argv[1],&statbuf);//将源文件中的文件属性信息填入statbuf中
mkdir(argv[2],statbuf.st_mode);//创建目录
timeby.actime = statbuf.st_atime;//文件数据的最后存取时间
timeby.modtime = statbuf.st_mtime;//文件数据的最后修改时间
utime(argv[2],&timeby);//用utime()函数修改目标文件的访问时间和修改时间
}
mycp(argv[1],argv[2]);//开始复制
}
printf("复制完成!\n");
return 0;
}