1、熟悉Linux文件系统提供的有关文件操作的系统调用。文件系统是使用计算机信息系统的重要接口,通过使用文件系统的系统调用命令操作文件,以达到对文件系统实现功能的理解和掌握。
2、了解Windows的文件系统时如何管理保存在磁盘、光盘等存储介质上的信息。并通过文件系统提供的各种API,对文件进行操作,深入理解Windows文件系统的功能和作用,理解文件系统的系统调用的功能。
完成一个目录复制命令mycp,包括目录下的文件和子目录。
在Windows下:调用CreateFile(), ReadFile(), WriteFile(), CloseHandle()等函数,完成文件的复制。
在Linux下:使用creat(),write(),read(),mkdir,readlink(),symlink()等系统调用,完成文件拷贝命令的实现,要求能够支持软连接文件的拷贝。
注意:不管时Windows下还是Linux下,要求每个文件不仅读写权限一致,而且时间属性一致。
操作系统:Windows10
操作系统:Linux-5.9.10
虚拟机:Ubuntu 18.04
IDE:VS 2019、vim
对于给定的一个文件,首先看其是否为目录文件,如果为目录文件,首先复制此目录文件的权限等信息,再调用编写的MyCp()函数递归复制文件夹中的内容。MyCp()函数的基本思想是,如果当前查询的文件为普通文件,则调用编写的CopyFile()函数复制文件内容、权限和时间等信息到指定的文件路径;如果当前查询的文件是软连接文件,则调用编写的CopyLinkFile()函数,将软连接文件复制到指定文件路径;如果当前查询的文件是目录文件,则递归调用MyCp()函数处理目录文件,直到文件不在是目录文件为止。对于给定的文件,如果是非目录文件,则程序会直接调用MyCp()函数复制文件到指定的路径中。由于不管是目录文件还是其他文件,如果在创建时就设置时间信息,后续的访问会导致文件的时间信息被修改,所以对于每一个目录文件,只有当其中的所有子文件都被复制后,才修改其时间属性。最后,关闭打开的文件或打开的文件句柄。
WIN32_FIND_DATA结构体:
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[ MAX_PATH ]; // 文件名
TCHAR cAlternateFileName[ 14 ]; // 格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
(1)FindFirstFile()
功能:查找指定路径的文件,成功则返回文件或目录句柄。
定义:
HANDLE FindFirstFile(
LPCTSTR lpFileName,// 目录名
LPWIN32_FIND_DATA lpFindFileData// 文件信息结构体);
(2)FindNextFile()
功能:查找FindFirstFile函数搜索后的下一个文件,成功则返回非0,否则返回0。
定义:
FindNextFile(
HANDLE hFindFile,//调用FindFirstFile返回的句柄
LPWIN32_FIND_DATA lpFindFileData//文件信息结构体
);
(3)CreateDirectory()
功能:创建文件夹()目录文件
定义:
BOOL CreateDirectory(
LPCTSTR lpPathName,//文件路径
LPSECURITY_ATTRIBUTES lpSecurityAttributes//常为NULL
);
(4)CreateFile()
功能:打开或创建文件,返回该文件的句柄
定义:
HANDLE CreateFile(
LPCTSTR lpFileName, // 指向文件名的指针
DWORD dwDesiredAccess, // 访问模式(写 / 读)
DWORD dwShareMode, // 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//安全属性
DWORD dwCreationDisposition,//创建方式
DWORD dwFlagsAndAttributes,//文件属性
HANDLE hTemplateFile//用于复制文件句柄
);
(5)SetFileTime()
功能:设置文件的时间属性,创建时间、最后一次修改和访问时间
定义:
Long SetFileTime(
Long hFile,//文件句柄
FILETIME lpCreationTime,//文件创建时间
FILETIME lpLastAccessTime,//文件最后一次访问时间
FILETIME lpLastWriteTime//文件最后一次修改时间
);
(6)SetFileAttributes()
功能:设置文件属性
定义:
BOOL SetFileAttributes(
LPCTSTR lpFileName, //文件路径
DWORD dwFileAttributes // 文件属性);
(7)ReadFile()
功能:从指定路径的文件中读取指定字节长度的数据
定义:
BOOL ReadFile(
HANDLE hFile, //文件的句柄
LPVOID lpBuffer, //用于保存读入数据的一个缓冲区
DWORD nNumberOfBytesToRead, //要读入的字节数
LPDWORD lpNumberOfBytesRead, //实际读取字节数的指针
LPOVERLAPPED lpOverlapped //一般置为NULL
);
(8)WriteFile()
功能:从文件指针指向的位置开始将数据写入到一个文件中
定义:
BOOL WriteFile(
HANDLE hFile, // 文件句柄
LPCVOID lpBuffer, // 数据缓存区指针
DWORD nNumberOfBytesToWrite, // 写入字节数
LPDWORD lpNumberOfBytesWritten, // 指向实际写入字节数
LPOVERLAPPED lpOverlapped // 结构体指针,一般为NULL);
在主函数中,将从命令行中接收三个参数,第一个(argv[0])是将要执行的可执行程序路径,第二个(argv[1])是等待被复制的文件路径(此后统一称为旧路径),第三个(argv[2])是将把旧文件复制到的目标路径(此后同一称为新路径)。
主函数(main函数)设计流程如下:
(1)在主函数中,首先判断输入的参数是否为3个,如果是则转(2),否则退出执行。
(2)调用FindFIrstFile(argv[1], &lpFindData)打开argv[1]路径下的旧文件,返回句柄hFindFile,并将其信息保存到lpFindNewData结构体中。如果成功打开则转(3),否则退出执行。
(3)调用FindFIrstFile(argv[2], &lpFindNewData)检查是否存在路径为argv[2]的文件,如果存在则退出执行,否则调用CreateDirectory(argv[2],NULL)在argv[2]路径上创建新文件。
(4)调用MyCp()递归函数复制就文件夹中的子文件。
(5)递归函数返回后,调用CreateFile()函数打开argv[2]路径上的新文件,返回句柄hDirFile,并调用SetFileTime()函数修改其时间属性,调用SetFileAttributes()函数设置文件属性。
(6)关闭句柄hFindFile以及hDirFile,将其值设置为INVALID_HANDEL_VALUE。
在此函数中,传入的参数OldPath为旧文件路径(源文件),NewPath为新文件路径(目标文件)。此外,设置char* 型的参数new_path和old_path,用于拼接目录中的子文件路径。WIN32_FIND_DATA型结构体lpFindData,句柄hFindData,BOOL型参数hFound。MyCp()函数流程如下:
(1)定义参数old_path,new_path,结构体lpFindData,句柄hFindData,hFound。
(2)通过lstrcpy(str1,str2)接口对old_path,new_path赋值,其值分别为OldPath,NewPath。
(3)调用FinFirstFile(old_path,&lpFindData)查找old_path路径下的文件并返回句柄hFindData,将文件信息存于lpFindData之中。
(4)如果hFindData值为INVALID_HANDLE_VALUE,则表示查找文件失败,程序退出执行;否则转(5)。
(5)调用hFIndData(hFinData,&lpFindData)查找OldPath路径上的文件夹中的下一个文件,并返回hFound(表示是否成功查找),若成功,则将文件信息存入lpFindData且转(6),否则说明已遍历当前文件夹中的最后一个文件。
(6)lpFindData.cFilename中存放当前将复制文件的相对路径,调用lstrcpy()拼接且结果为:old_path+’/’+lpFindData.cFilename,new_path+’/’+lpFindData.cFilename,拼接后的路径仍然赋值给old_path和new_path。
(7)调用IsChildDir(lpFindData)判断当前文件是否为子目录文件,如果为子目录文件,首先在new_path路径下创建一个新的目录文件,后递归调用MyCp()函数,对old_path下的目录文件(此时old_path对应的目录文件即当前的子目录文件)复制到new_path,复制完成后,再修改new_path下的目录文件的时间属性。在修改时间属性时,需要通过CreateFile()函数打开new_path下的文件并返回句柄hDirFile,后调用SetFileTime()函数设置时间属性(如何复制时间属性将在后续阐述),调用SetFileAttributes()函数设置文件属性。值得注意的是,在打开文件夹时,应设置dwCreationDisposition字段为OPEN_EXISTING,设置dwFlagsAndAttributes字段为FILE_FLAG_BACKUP_SEMANTICS。
(8)如果不为目录文件,则为其他文件,调用CopyFile()函数完成文件的复制。
(9)关闭句柄hFindData,设置其值为INVALID_HANDLE_VALUE,执行结束并返回。
在MyCp()函数中,涉及到判断是否为子目录的函数IsChildDir()定义如下:
BOOL IsChildDir(WIN32_FIND_DATA& lpFindData) {
return (
((lpFindData.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY) != 0) &&
(lstrcmp(lpFindData.cFileName, __TEXT(".")) != 0) &&
(lstrcmp(lpFindData.cFileName, __TEXT("..")) != 0));
}
(1)定义句柄hOldFile(指向旧文件的句柄)、hNewFile(指向新文件的句柄),WIN32_FIND_DATA型参数lpFindData(存放文件信息)。
(2)调用CreateFile()打开OldPath路径下的旧文件,返回句柄hOldFile。
(3)调用CreateFile()在NewPath路径下创建新文件,返回句柄hNewFile。
(4)如果hOldFile和hNewFile不是无效值,转(5),否则退出执行。
(5)循环调用ReadFile()从旧文件中读并且调用WriteFile()函数往新文件中写,设置每次读写的大小BUFSIZE为1024,记录每次实际读写的大小dwxfer,当BUFSIZE不等于dwxfer时,说明复制结束,退出循环。
(6)调用SetFileTime()函数设置文件时间属性,调用SetFileAttributes()函数设置文件属性。
(7)关闭文件句柄hNewFile、hOldFile,设置其值为INVALID_HANDLE_VALUE。
在lpFindData结构体中,保存有旧文件的创建时间lpFindData.ftCreationTime、最近访问时间lpFindData.ftLastAccessTime和最近修改时间lpFindData.ftLastWriteTime,可以借此用于修改新文件的时间属性。此外,还保存有旧文件属性信息lpFindData.dwFileAttributes,可用于修改新文件属性。
(1)stat结构体
struct stat {
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; /* protection -保护模式?*/
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; /* user ID of owner -user id*/
gid_t st_gid; /* group ID of owner - group id*/
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
time_t st_atime; /* time of last access -最近存取时间*/
time_t st_mtime; /* time of last modification -最近修改时间*/
time_t st_ctime; /* time of last status change - */
};
(2)utimbuf结构体
struct utimbuf{
time_t atime; /*access time :访问时间*/
time_t modtime; /*modification time : 更改时间*/
};
(3)dirent结构体
struct dirent{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
};
;
(1) DIR * opendir(const char * name);
功能:打开指定的目录,并返回DIR*型的目录流。
(2)int mkdir (const char *filename, mode_t mode)
功能:创建名为filename的目录,创建成功返回0,失败返回-1。
(3)int stat(const char *path, struct stat buf)
参数:文件路径名,stat类型结构体。
功能:获取path路径下的文件属性信息,成功返回0,失败返回-1。
(4)Int utime(const char filename, const struct utimbuf times)
参数:文件路径名,utimbuf类型结构体。
功能:修改文件的时间属性,包括最后一词访问和修改时间,成功则返回0,失败则返回-1。
(5)int closedir(DIR dir)
功能:关闭目录文件指针。
(6)struct dirent readdir(DIR dir_handle);
功能:读取下一个目录,返回DIR 类型的指针。
(7)S_ISDIR()
功能:判断一个文件是否为目录文件。
(8)S_ISLNK()
功能:判断一个文件是否为符号链接文件。
(9)int lstat(const char *path, struct stat *buf);
功能:与stat()相似,只是path下的文件是符号链接文件时,lstat()函数获取该符号链接文件的信息。
*(10)int readlink(const char path, char buf, size_t bufsize)
功能:读取符号链接文件本身的信息,组合了打开、读和关闭的所有操作。成功则返回读到缓冲区buf的字节数,否则返回-1。
(11)int syslink(const char *oldpath, const char *sympath)
参数:已存在的文件路径名,将要创建的符号链接文路径名。
功能:创建一个符号链接文件。成功时返回0,否则返回-1。
*(12)int chmod(const char filename, int pmode)
参数:文件路径名,文件读取许可权限。
功能:改变文件的读写许可权限,成功改变则返回0,否则返回-1。
(13) lutimes(const char *filename, struct timeval *tv)
参数:符号链接文件路径名,timeval结构体
功能:改变符号链接文件的时间属性,成功返回0,否则返回-1。
与Windows相同,主函数从命令行接收三个参数,第一个(argv[0])是将要执行的可执行程序路径,第二个(argv[1])是等待被复制的文件路径(此后统一称为旧路径),第三个(argv[2])是将把旧文件复制到的目标路径(此后同一称为新路径)。其设计流程如下:
(1)判断从命令行接受的参数是否为3个,若是,转(2),否则退出之执行。
(2)定义stat结构体statbuf,用于修改文件属性;utimbuf结构体uTime,用于修改文件的时间属性,DIR指针dirptr、dirptr2指向打开的文件夹。
(3)调用opendir(argv[1])打开旧文件夹并返回指向该文件夹目录流的指针dirptr,如果dirptr为NULL则退出执行,否则说明打开成功,继续下一步。
(4)调用opendir(argv[2])打开新文件夹,返回指向该文件夹目录流的指针dirptr2,如果dirptr2为NULL,则调用mkdir(argv[2],statbuf.st_mode)函数在argv[2]路径下创建文件夹,st_mode指定新创建文件夹权限;否则退出执行。
(5)调用自己编写的MyCopy()函数复制文件夹中的内容。
(6)调用stat(argv[1],&statbuf)函数将argv[1]路径下的目录文件信息存入statbuf中,并用statbuf中的时间属性更新uTime,调用utime(argv[2],uTime)接口修改argv[2]路径下文件夹的时间属性。
(7)关闭closedir()接口关闭指向文件夹的指针dirptr和dirptr2。
(1)定义stat结构参数statbuf,用于存储文件属性;utimbuf结构体参数uTime,用于记录文件的时间属性;dirent结构体指针entry,指向存储文件夹中的文件目录信息,DIR*形式的目录流dirptr;old_path存放旧文件路径,new_path存放新文件路径,并调用strcpy()函数将OldPath、NewPath分别复制给old_path和new_path。
(2)调用opendir()打开old_path路径下的文件夹,返回指针dirptr。
(3)调用readdir(dirptr)读取文件中下一条目录信息,并返回给entry,若entry为空,则说明已到目录结尾,转();否则entry为一个指向dirent结构的指针,此结构包含该目录项指向的文件信息,转(4)。
(4)如果当前的目录项既非父目录也非OldPath路径下的文件夹,则说明是OldPath中的文件的目录项,转(5),否则转(3)。
(5)entry->name为目录中的文件名,将此文件名与路径连接,得到该文件的路径以及目标路径。其中:
old_path = old_path + ‘/’ + entry->name
new_path = new_path + ‘/’ +entry->name
(6)调用lstat(old_path,&statbuf)接口,将old_path路径下的文件信息保存到statbuf中,根据statbuf.st_mode字段来判断文件类型。
(7)如果是子目录文件(调用S_ISDIR(statbuf.st_mode)返回真),调用mkdir(new_path)接口在new_path路径下创建一个新的目录文件,再递归调用MyCopy()函数完成子目录文件的复制,复制完成后,更新uTime,并调用utime()接口修改文件时间属性;若不是子目录文件,则转(8)。
(8)如果是符号链接文件(调用S_ILINK(statbuf.st_mode)返回为真),调用自己编写的CopyLinkFile()接口进行复制;否则为其他文件,转(9)。
(9)对于其他文件,调用CopyFile()接口进行文件复制。
(10)已完成对OldPath路径下的附件的复制,关闭打开的指针dirptr。
传入参数OldPath为旧文件路径,NewPath为新文件的路径。
(1)struct stat Statbuf;//用于存放旧文件的各项属性信息
struct utimbuf uTime;//用于记录旧文件的最后修改和最后访问时间int NewFd,OldFd;//分别为新文件描述符、旧文件描述符
int size;//为实际从文件中读取的字节数
char buffer[READSIZE];//存放数据的缓冲区,READSIZE为1024
(2)调用stat(OldPath,&statbuf)接口将OldPath路径下的文件属性信息存入statbuf中;调用creat(NewPath,statbuf.st_mode)在NewPath路径下创建新文件,新文件的权限应与旧文件(statbuf.st_mode)一致,并获取新文件描述符NewFd。
(3)调用open()接口以只读方式打开OldPath路径下的文件,如果打开失败,则结束执行,如果打开成功,这返回旧文件描述符OldFd。
(4)调用read(OldFd,buffer,READSIZE)函数从旧文件中读取READSIZE字节大小的内容,并将读取的内容暂存于buffer缓冲区中,OldFd为文件描述符,并返回实际读取的字节数size。如果size大于0,转(5),否则转(6)
(5)调用write(NewFd,&buffer,size)将buffer缓冲区的内容写入文件描述符为NewFd的新文件中,数据大小为size。转(4)。
(6)更新uTime,并调用utime(NewPath,&uTime)修改新文件的时间属性。
(7)关闭新旧文件描述符。
实际上,在修改文件时间属性时,应先更新uTime的内容:
uTime.actime = statbuf.st_atime;
uTime.modtime = statbuf.st_mtime;
传入参数中,LinkPath为软连接文件的路径,NewPath为新文件路径。
(1)定义参数如下:
struct stat statbuf;//存放软连接文件的各项属性信息的结构体
struct timeval tv[2];//记录软连接文件的最后访问和修改
char path_buf[LinkPathLen];//存放软连接文件中的文件路径
(2)对于软连接文件,lstat()函数得到的是当前文件的属性信息,并非连接文件的属性信息,所以使用lstat(LinkPath,&statbuf)接口得到当前文件的属性信息,并保存于statbuf中。
(3)调用readlink(LinkPath,path_buf,LinkPathLen)读取连接文件的内容(实际上是文件中保存的路径),读取的最大长度为LinkPathLen,将内容保存在path_buf缓冲区中。
(4)symlink(path_buf,NewPath)建立新的软连接文件,新文件路径为NewPath,链接文件的路径为path_buf。成功创建,则转(5),否则退出执行。
(5)chmod(NewPaht,statbuf。st_mode)设置新文件的权限信息,且权限与旧的符号链接文件(statbuf.st_mode)一致。
(6)在tv结构体数组中记录旧文件的时间属性:
tv[0].tv_sec = statbuf.st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = statbuf.st_mtime;
tv[1].tv_usec = 0;
并调用lutimes(NewPath,tv)修改新文件的时间属性与旧文件一致。
将F:/Dev-cpp/Project1文件夹复制到F:/VS 2019中,过程如下:
将/home/Test2/Os_3文件夹复制到/home/Test2/OS3
在Windows上实现时,应该调用SetFileAttributes()完成对文件存去权限的设置,很多时候,容易忘记修改新文件的权限。
在使用readlink()函数时,不需要再打开文件和关闭文件,因为readlink()系统调用将完成打开文件、读取和关闭文件的一系列操作,在读取链接文件本身信息的时候才使用readlink()系统调用。
stat()调用与lstat()调用很相似,不同之处在于,对于符号链接文件,stat()调用将统计符号链接文件所指向的普通文件的信息,而lstat()文件只统计符号链接文件本身的信息。所以,为了取得符号链接文件中的路径,应该使用lstat()而不是stat()。
不管是Windows还是Linux,一个目录文件的目录项总是包含本文件目录(.)和父目录(…),只是一般情况下,这两个目录项都是隐藏的,在Linux命令行中调用ls命令,或在Windows命令行中调用dir命令,都能够显示这两个目录项。
在复制文件时,应该递归复制,因为目录下可能会有子目录。
通过实验,掌握了Linux、Windows中对文件和目录操作的系统调用的使用,理解了其原理。理解和掌握了文件系统的实现功能。
Linux下的源码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define READSIZE 1024
#define LinkPathLen 1024
///
/// OldPath为待复制文件路径
/// NewPath为新文件路径
///
///
///
void CopyFile(char* OldPath, char* NewPath) {
struct stat statbuf;///stat用于存储文件各项属性信息
struct utimbuf uTime;///uTime记录文件的最后修改时间和最后存取时间
int NewFd;//新文件描述符
int OldFd;//旧文件描述符
int size = 0;//每次从文件中读取的数据量
char buffer[READSIZE];//缓冲区,用于存放文件里的数据
memset(buffer, 0, sizeof(buffer));
stat(OldPath, &statbuf);
NewFd = creat(NewPath, statbuf.st_mode);//创建新文件,返回文件描述符
//打开文件
if ((OldFd = open(OldPath, O_RDONLY)) < 0) {
printf("open file:%s error!\n", OldPath);
exit(-1);
}
//复制文件内容
while ((size = read(OldFd, buffer, READSIZE)) > 0) {
write(NewFd, &buffer, size);
}
//修改新文件时间属性
uTime.actime = statbuf.st_atime;
uTime.modtime = statbuf.st_mtime;
utime(NewPath, &uTime);
///关闭文件
close(OldFd);
close(NewFd);
}
void CopyLinkFile(char* LinkPath, char* NewPath) {
struct stat statbuf;
struct timeval tv[2];
char path_buf[LinkPathLen];
memset(path_buf, 0, sizeof(path_buf));
lstat(LinkPath, &statbuf);
readlink(LinkPath, path_buf, LinkPathLen);
if (symlink(path_buf, NewPath) == -1) {
printf("create link error!\n");
_exit(-1);
}
printf("软连接文件复制成功\n");
chmod(NewPath, statbuf.st_mode);
tv[0].tv_sec = statbuf.st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = statbuf.st_mtime;
tv[1].tv_usec = 0;
lutimes(NewPath, tv);
}
void MyCopy(char *OldPath, char *NewPath) {
struct stat statbuf;///stat用于存储文件各项属性信息
struct stat copybuf;
struct utimbuf uTime;///uTime记录文件的最后修改时间和最后存取时间
struct dirent* entry = NULL;///DR存储目录中的文件信息
DIR* dirptr = NULL;///指向打开的目录
char old_path[128], new_path[128];//存放路径
strcpy(old_path, OldPath);
strcpy(new_path, NewPath);
dirptr = opendir(old_path);
while ((entry = readdir(dirptr)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
//路径名拼接
strcpy(new_path, NewPath);
strcpy(old_path, OldPath);
strcat(old_path, "/");
strcat(new_path, "/");
strcat(old_path, entry->d_name);
strcat(new_path, entry->d_name);
lstat(old_path, &statbuf);
//是子目录
if(S_ISDIR(statbuf.st_mode)) {
printf("Director: %s,正在拷贝...\n",old_path);
stat(old_path, ©buf);
mkdir(new_path, copybuf.st_mode);
MyCopy(old_path, new_path);
uTime.actime = copybuf.st_atime;
uTime.modtime = copybuf.st_mtime;
utime(new_path, &uTime);
printf("Directory: %s,拷贝完成\n",old_path);
}
//是符号链接文件
else if (S_ISLNK(statbuf.st_mode)) {
printf("LinkFileName: %s,正在拷贝...\n",old_path);
CopyLinkFile(old_path, new_path);
}
//是其他文件
else {
printf("FileName: %s,正在拷贝...\n",old_path);
CopyFile(old_path, new_path);
printf("FileName: %s,拷贝完成\n",old_path);
}
}
closedir(dirptr);
}
int main(int arcg, char* argv[]) {
///如果命令行参数超过三个
if (arcg != 3) {
printf("arcg error!\n");
exit(-1);
}
///定义参数
struct stat statbuf;///stat用于存储文件各项属性信息
struct utimbuf uTime;///uTime记录文件的最后修改时间和最后存取时间
struct dirent *DR;///DR存储目录中的文件信息
DIR *dirptr = NULL;///指向打开的目录
DIR *dirptr2 = NULL;///指向打开的目录
//打开目录
if ((dirptr = opendir(argv[1])) == NULL) {
printf("open dir error!\n");
exit(-1);
}
//创建目录,前提是为该名字的目录不存在
if ((dirptr2 = opendir(argv[2])) == NULL) {
//得到argv[1]指向的文件信息,存储时间信息
stat(argv[1], &statbuf);
uTime.actime = statbuf.st_atime;
uTime.modtime = statbuf.st_mtime;
//创建目录
if (mkdir(argv[2], statbuf.st_mode) < 0) {
printf("create dir error!\n");
exit(-1);
}
}
else {
printf("Filename duplication!\n");
exit(-1);
}
//复制目录中的内容
MyCopy(argv[1], argv[2]);
utime(argv[2], &uTime);//修改目录时间属性
printf("copy finished!\n");
closedir(dirptr);
closedir(dirptr2);
return 0;
}
windows下的源码
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define PATH_SIZE 512
#define BUFSIZE 1024
///
/// OldPath为源文件路径,该文件不是目录
/// NewPath为新创建的文件路径
///
///
///
void CopyFile(char* OldPath, char* NewPath) {
WIN32_FIND_DATA lpFindData;
HANDLE hFind = FindFirstFile(OldPath, &lpFindData);
//打开源文件
HANDLE hOldFile = CreateFile(
OldPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
//创建新文件
HANDLE hNewFile = CreateFile(
NewPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hNewFile != INVALID_HANDLE_VALUE && hOldFile != INVALID_HANDLE_VALUE) {
char buffer[BUFSIZE];
DWORD dwxfer(0);
//读源文件内容
while (true) {
ReadFile(
hOldFile,
buffer,
BUFSIZE,
&dwxfer,
NULL);
//向新文件中写
WriteFile(
hNewFile,
buffer,
dwxfer,
&dwxfer,
NULL);
if (dwxfer != BUFSIZE) break;
}
//修改文件时间属性
SetFileTime(
hNewFile,
&lpFindData.ftCreationTime,
&lpFindData.ftLastAccessTime,
&lpFindData.ftLastWriteTime);
SetFileAttributes(NewPath, lpFindData.dwFileAttributes);
}
CloseHandle(hNewFile);
CloseHandle(hOldFile);
hOldFile = INVALID_HANDLE_VALUE;
hNewFile = INVALID_HANDLE_VALUE;
}
///
/// 判断是否为子目录文件
///
///
///
BOOL IsChildDir(WIN32_FIND_DATA& lpFindData) {
return (
((lpFindData.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY) != 0) &&
(lstrcmp(lpFindData.cFileName, __TEXT(".")) != 0) &&
(lstrcmp(lpFindData.cFileName, __TEXT("..")) != 0));
}
///
/// OldPath为待复制的源文件目录路径
/// NewPath为新创建的目录路径
///
///
///
void MyCp(char* OldPath, char* NewPath) {
char old_path[PATH_SIZE], new_path[PATH_SIZE];
WIN32_FIND_DATA lpFindData;
lstrcpy(old_path, OldPath);
lstrcpy(new_path, NewPath);
strcat(old_path, "/*.*");
HANDLE hFindData;
BOOL hFound = 0;
hFindData = FindFirstFile(old_path, &lpFindData);
if (hFindData != INVALID_HANDLE_VALUE) {
//查找下一个文件
while ((hFound = FindNextFile(hFindData, &lpFindData)) != 0) {
//路径拼接
lstrcpy(old_path, OldPath);
lstrcpy(new_path, NewPath);
lstrcat(old_path, "/");;
lstrcat(new_path, "/");
lstrcat(old_path, lpFindData.cFileName);
lstrcat(new_path, lpFindData.cFileName);
//判断文件是否为目录文件
if (IsChildDir(lpFindData)) {
printf("child directory:%s begain copy\n", lpFindData.cFileName);
CreateDirectory(new_path, NULL);
MyCp(old_path, new_path);
//修改子目录时间属性
HANDLE hDirFile = CreateFile(
new_path,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
SetFileTime(
hDirFile,
&lpFindData.ftCreationTime,
&lpFindData.ftLastAccessTime,
&lpFindData.ftLastWriteTime);
SetFileAttributes(new_path, lpFindData.dwFileAttributes);
CloseHandle(hDirFile);
hDirFile = INVALID_HANDLE_VALUE;
printf("child directory:%s copy finished\n", lpFindData.cFileName);
}
else {
CopyFile(old_path, new_path);
cout << "Filename:" << lpFindData.cFileName << "copy finished" << endl;
}
}
}
else {
cout << "find file error!" << endl;
exit(-1);
}
CloseHandle(hFindData);
hFindData = INVALID_HANDLE_VALUE;
}
/// <summary>
/// argv接收从命令行传来的三个参数,执行程序 旧文件路径 新文件路径
/// </summary>
/// <param name="argc"></param>
/// <param name="argv"></param>
/// <returns></returns>
int main(int argc, char* argv[]) {
WIN32_FIND_DATA lpFindData, lpFindNewData;
HANDLE hFindFile;
//命令行输入的参数只能是3个,
if (argc != 3) {
cout << "input parameters are error!" << endl;
exit(-1);
}
//查找源文件
if ((hFindFile = FindFirstFile(argv[1], &lpFindData)) == INVALID_HANDLE_VALUE) {
cout << "Finding source file error!" << endl;
exit(-1);
}
//创建新目录
if (FindFirstFile(argv[2], &lpFindNewData) == INVALID_HANDLE_VALUE) {
CreateDirectory(argv[2], NULL);
cout << "Creating directory successfully" << endl;
}
MyCp(argv[1], argv[2]);
HANDLE hDirFile = CreateFile(
argv[2],
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
SetFileTime(
hDirFile,
&lpFindData.ftCreationTime,
&lpFindData.ftLastAccessTime,
&lpFindData.ftLastWriteTime);
SetFileAttributes(argv[2], lpFindData.dwFileAttributes);
CloseHandle(hDirFile);
CloseHandle(hFindFile);
hFindFile = INVALID_HANDLE_VALUE;
hDirFile = INVALID_HANDLE_VALUE;
cout << "copy finished!" << endl;
return 0;
}