文件复制

文件复制

1. 实验目的

了解在Windows中,文件系统如何保存在磁盘、光盘等存储介质上的信息。并通过文件系统提供的各种API,对文件进行同步和异步读写,深入理解Windows文件系统的功能和作用,掌握同步I/O和异步I/O的特点

熟悉Linux文件系统提供的有关文件操作的系统调用。文件系统是使用计算机信息系统的重要接口。通过使用文件系统的系统调用命令操作文件,以达到对文件系统实现功能的理解和掌握

2. 实验内容

完成一个目录复制命令mycp,包括目录下的文件和子目录,运行结果如下:

beta@bugs.com [~/]# ls –l sem
total 56
drwxr-xr-x 3 beta beta 4096 Dec 19 02:53 ./
drwxr-xr-x 8 beta beta 4096 Nov 27 08:49 ../
-rw-r--r-- 1 beta beta 128 Nov 27 09:31 Makefile
-rwxr-xr-x 1 beta beta 5705 Nov 27 08:50 consumer* 
-rw-r--r-- 1 beta beta 349 Nov 27 09:30 consumer.c 
drwxr-xr-x 2 beta beta 4096 Dec 19 02:53 subdir/ 
beta@bugs.com [~/]# mycp sem target
beta@bugs.com [~/]# ls –l target
total 56
drwxr-xr-x 3 beta beta 4096 Dec 19 02:53 ./
drwxr-xr-x 8 beta beta 4096 Nov 27 08:49 ../
-rw-r--r-- 1 beta beta 128 Nov 27 09:31 Makefile
-rwxr-xr-x 1 beta beta 5705 Nov 27 08:50 consumer*
-rw-r--r-- 1 beta beta 349 Nov 27 09:30 consumer.c
drwxr-xr-x 2 beta beta 4096 Dec 19 02:53 subdir/

说明:

  • Linux:createreadwrite等系统调用,要求支持软链接
  • Windows:CreateFile()ReadFile()WriteFile()CloseHandle()等函数
  • 特别注意复制后,不仅权限一致,而且时间属性也一致

3. 实验环境

本实验基于本机macOS系统和Windows虚拟机完成,具体实验环境如下:

3.1 Linux环境

macOS环境配置如下:

  • 操作系统:macOS
  • 内存容量:8GB
  • 处理器:2.9GHz Intel Core i5
  • 硬盘容量:500GB

3.2 虚拟机环境

Windows虚拟机环境配置如下:

  • 虚拟机软件:VMware Fusion 11
  • 虚拟机操作系统:Windows 7 旗舰版
  • 虚拟机内存:4GB
  • 虚拟机硬盘容量:60GB

4.程序设计和实现

4.1 Windows实现

4.1.1 实验原理

接受到的源目录后,打开源目录遍历目录下所有的目录和文件并完成复制。若遍历到目录,则递归调用复制目录,若遍历到文件,则调用复制文件

对接收到的目标目录路径,若不存在该目录,则新创建一个同名目录,并更新其权限和时间同源目录一致

复制目录时,若在目标路径下不存在该目录,则要创建一个同名目录,并更新其权限和时间同复制源目录一致

复制文件时,需要调用读文件和写文件函数,将源文件内容写入目标文件,并更新其权限和时间同复制源文件一致

4.1.2 相关数据结构介绍

文件信息

Windows系统使用WIN32_FIND_DATA数据结构保存FindFirstFileFindNextFile找到的文件信息

定义如下:

typedef struct _WIN32_FIND_DATAA {
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  CHAR     cFileName[MAX_PATH];
  CHAR     cAlternateFileName[14];
  DWORD    dwFileType;
  DWORD    dwCreatorType;
  WORD     wFinderFlags;
} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA;

说明:(只说明部分用到的成员)

  • dwFileAttributes:文件的文件属性
  • nfileSizeHigh:文件大小的高位DWORD值,以字节为单位
  • nfileSizeLow:文件大小的低位DWORD值,以字节为单位
  • 头文件:minwinbase.h包含于Windows.h

4.1.3 API介绍

查找文件

Windows系统中,用FindFirstFileFindNextFile函数遍历目录下的文件或目录

语法如下:

HANDLE FindFirstFileA(
  LPCSTR             lpFileName,
  LPWIN32_FIND_DATAA lpFindFileData
);
BOOL FindNextFileA(
  HANDLE             hFindFile,
  LPWIN32_FIND_DATAA lpFindFileData
);

说明:

  • lpFileName:目录或路径以及文件名,文件名可以包含通配符
  • lpFindFileData:指向WIN32_FIND_DATA结构的指针,该结构接收相关找到的文件或目录的信息
  • 返回值
    • 若函数成功,则返回值用于FindNextFileFindClose使用的句柄,而lpFindFileData包含找到文件或目录的信息
    • 若函数失败,则返回值为INVALID_HANDLE_VALUE
  • 头文件:fileapi.h包含与Windows.h
创建或打开文件

Windows系统使用CreateFile创建或打开一个文件或I/O设备

语法如下:

HANDLE CreateFileA(
  LPCSTR                lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile
);

说明:

  • lpFileName:要创建或打开的文件或设备名称
  • dwDesiredAccess:请求访问文件或设备的权限
    • 常用值是GEnERIC_READGENERIC_WRITE或两者的或
  • dwShareMode:请求的文件或设备的共享模式,可以是读取,写入,删除等
  • lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构的指针,描述安全性
  • dwCreationDisposition:对存在或不存在的文件或设备指向的操作
  • dwFlagAndAttributes:文件或设备属性和标志,文件中最常见默认值为FILE_ATTRIBUTE_NORMAL
  • hTemplateFile:具有GENERIC_READ访问权限的模版文件的有效句柄,可以为NULL
  • 返回值
    • 若函数成功,则返回指定文件的打开句柄
    • 若函数失败,则返回INVALID_HANDLE_VALUE
  • 头文件:fileapi.h包含于Windows.h
读文件

Windows系统使用ReadFile从指定文件或I/O设备读取数据

语法如下:

BOOL ReadFile(
  HANDLE       hFile,
  LPVOID       lpBuffer,
  DWORD        nNumberOfBytesToRead,
  LPDWORD      lpNumberOfBytesRead,
  LPOVERLAPPED lpOverlapped
);

说明:

  • hFile:文件或设备的句柄
  • lpBuffer:指向缓冲区的指针,该缓冲区接收从文件或设备读取的数据
  • nNumberOfBytesToRead:要读取的最大字节数
  • lpNumberOfBytesRead:指向变量的指针,该变量接收使用同步hFile参数时读取的字节
  • lpOverlapped:若使用FILE_FLAG_OVERLAPPED打开hFile参数,则需要指向一个OVERLAPPED结构,否则可以为NULL
  • 返回值
    • 函数成功,则返回非零(TRUE)
    • 函数失败,则返回零(FALSE)
  • 头文件:fileapi.h包含于Windows.h
写文件

Windows操作系统使用WriteFile函数将数据写入指定的文件或I/O设备中

语法如下:

BOOL WriteFile(
  HANDLE       hFile,
  LPCVOID      lpBuffer,
  DWORD        nNumberOfBytesToWrite,
  LPDWORD      lpNumberOfBytesWritten,
  LPOVERLAPPED lpOverlapped
);

说明:

  • hFile:文件或设备的句柄
  • lpBuffer:指向要写入文件或设备的数据的缓冲区指针
  • nNumberOfBytesToRead:要写入文件或设备的最大字节数
  • lpNumberOfBytesRead:指向变量的指针,该变量接收使用同步hFile参数时读取的字节
  • lpOverlapped:若使用FILE_FLAG_OVERLAPPED打开hFile参数,则需要指向一个OVERLAPPED结构,否则可以为NULL
  • 返回值
    • 函数成功,则返回非零(TRUE)
    • 函数失败,则返回零(FALSE)
  • 头文件:fileapi.h包含于Windows.h

修改文件时间

Windows系统使用SetFileTime函数进行文件时间属性修改

语法如下:

    BOOL SetFileTime(
      HANDLE         hFile,
      const FILETIME *lpCreationTime,
      const FILETIME *lpLastAccessTime,
      const FILETIME *lpLastWriteTime
    );

说明:

  • hFile:要修改文件的句柄
  • lpCreationTime:文件创建时间
  • lpLastAccessTime:文件最后访问时间
  • lpLastWriteTime:文件最后写入时间

4.1.4 程序代码

程序代码以附件方式给出,共一个文件mycp.cpp,如下:

#include
#include
#include
#include
#include

#define BUFF_SIZE 4096

void copyFile(const char *sourceFile,const char *destinationFile){
    WIN32_FIND_DATA lpfilefinddata;
    HANDLE hfirstfile=FindFirstFile(sourceFile,&lpfilefinddata);
    HANDLE hsourcefile=CreateFile(sourceFile,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    HANDLE hdestinationfile=CreateFile(destinationFile,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    LONG filesize=lpfilefinddata.nFileSizeLow-lpfilefinddata.nFileSizeHigh;
    DWORD word;
    int *buff=new int[filesize];
    ReadFile(hsourcefile,buff,filesize,&word,NULL);
    WriteFile(hdestinationfile,buff,filesize,&word,NULL);
    SetFileTime(hdestinationfile,&lpfilefinddata.ftCreationTime,&lpfilefinddata.ftLastAccessTime,&lpfilefinddata.ftLastWriteTime);
    CloseHandle(hfirstfile);
    CloseHandle(hsourcefile);
    CloseHandle(hdestinationfile);
}

void copyDir(const char *sourceDir,const char *destinationDir){
    WIN32_FIND_DATA lpfilefinddata;
    char tempsourceDir[BUFF_SIZE],tempdestinationDir[BUFF_SIZE];
    memset(tempsourceDir,0,sizeof(tempsourceDir));
    lstrcpy(tempsourceDir,sourceDir);
    lstrcat(tempsourceDir,"\\*.*");
    HANDLE hfirstfile=FindFirstFile(tempsourceDir,&lpfilefinddata);
    while(FindNextFile(hfirstfile,&lpfilefinddata)!=0){
        if(lpfilefinddata.dwFileAttributes==16){        //文件属性为16表示是一个目录
            if (strcmp(lpfilefinddata.cFileName,".")!=0&&strcmp(lpfilefinddata.cFileName,"..")!=0) {
                memset(tempsourceDir,0,sizeof(tempsourceDir));
                memset(tempdestinationDir,0,sizeof(tempdestinationDir));
                lstrcpy(tempsourceDir,sourceDir);
                lstrcpy(tempdestinationDir,destinationDir);
                lstrcat(tempsourceDir,"\\");
                lstrcat(tempdestinationDir,"\\");
                lstrcat(tempsourceDir,lpfilefinddata.cFileName);
                lstrcat(tempdestinationDir,lpfilefinddata.cFileName);
                CreateDirectory(tempdestinationDir,NULL);
                HANDLE hdestinationfile=CreateFile(tempdestinationDir,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
                HANDLE hsourcefile=CreateFile(tempsourceDir,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
                FILETIME create,access,write;
                GetFileTime(hsourcefile,&create,&access,&write);
                SetFileTime(hdestinationfile,&create,&access,&write);
                printf("sourcefile:%s\ndestinationfile:%s\n",tempsourceDir,tempdestinationDir);
                copyDir(tempsourceDir,tempdestinationDir);
            }
        }
        else{
            memset(tempsourceDir,0,sizeof(tempsourceDir));
            memset(tempdestinationDir,0,sizeof(tempdestinationDir));
            lstrcpy(tempsourceDir,sourceDir);
            lstrcpy(tempdestinationDir,destinationDir);
            lstrcat(tempsourceDir,"\\");
            lstrcat(tempdestinationDir,"\\");
            lstrcat(tempsourceDir,lpfilefinddata.cFileName);
            lstrcat(tempdestinationDir,lpfilefinddata.cFileName);
            printf("sourcefile:%s\ndestinationfile:%s\n",tempsourceDir,tempdestinationDir);
            copyFile(tempsourceDir,tempdestinationDir);
        }
    }
    CloseHandle(hfirstfile);
}

int main(int argc, char const *argv[])
{
    if(argc!=3){
        printf("Please enter VALID parameters\n");
        printf("For example: mycp.exe sourceDir destinationDir\n");
        exit(0);
    }
    WIN32_FIND_DATA lpfindfiledata;
    if(FindFirstFile(argv[1],&lpfindfiledata)==INVALID_HANDLE_VALUE){
        printf("Can not find the source directory\n");
        exit(0);
    }
    if(FindFirstFile(argv[2],&lpfindfiledata)==INVALID_HANDLE_VALUE){
        printf("Create new directory %s\n",argv[2]);
        CreateDirectory(argv[2],NULL);
    }
    copyDir(argv[1],argv[2]);
    printf("Copy is done\n");
    return 0;
}

运行方式如下:

g++ mycp.cpp -o mycp.exe
.\mycp.exe sourcedir destinationdir

4.1.5 实验结果

创建源目录,如下:

文件复制_第1张图片

使用mycp.exe复制,如下:

文件复制_第2张图片

复制结果如下:

文件复制_第3张图片

4.2 macOS实现

4.2.1 实验原理

接受到的源目录后,打开源目录(注意要支持软连接)遍历目录下所有的目录和文件并完成复制。若遍历到目录,则递归调用复制目录,若遍历到文件,则调用复制文件

对接收到的目标目录路径,若不存在该目录,则新创建一个同名目录,并更新其权限和时间同源目录一致

复制目录时,若在目标路径下不存在该目录,则要创建一个同名目录,并更新其权限和时间同复制源目录一致

复制文件时,需要调用读文件和写文件函数,将源文件内容写入目标文件,并更新其权限和时间同复制源文件一致

4.2.2 相关数据结构介绍

DIR结构体

类似于FILE结构体,是一个内部结构体,用于保存当前被读取的目录相关信息

定义如下:

struct __dirstream{
  void* __fd;
  char* __data;
  int __entry_data;
  char* __ptr;
  int __entry_ptr;
  size_t __allocation;
  size_t __size;
  __libc_lock_define (,__lock)
};
typedef struct __dirstream DIR;

本结构体为内部结构体,实验中不详细说明其功能

文件属性

Linux系统使用struct stat结构体描述一个文件系统中的文件属性的结构

定义如下:

struct stat{
  mode_t st_mode;
  ino_t st_ino;
  dev_t st_dev;
  dev_t st_rdev;
  nlink_t st_nlink;
  uid_t st_uid;
  git_t st_gid;
  off_t st_size;
  time_t st_atime;
  time_t st_mtime;
  time_t st_ctime;
  blksize_t st_blksize;
  blkcnt_t st_blocks;
};

说明:

  • st_mode:文件对应的模式,包含权限信息
  • st_ino:inode节点号
  • st_dev:设备号
  • st_rdev:特殊设备号
  • st_nlink:文件链接数
  • st_uid:文件所有者
  • st_gid:文件所有者对应的组
  • st_size:文件对应的字节数
  • st_atime:文件最后被访问的时间
  • st_mtime:文件内容最后被修改的时间
  • st_ctime:文件状态改变的时间
  • st_blksize:文件内容对应的块大小
  • st_blocks:文件对应的块数量
  • 实验通过该数据结构获取复制源文件的属性信息
  • 头文件:sys/stat.h
文件时间

Linux系统使用utimbuf作为文件时间属性结构体

定义如下:

struct utimbuf{
  time_t actime;
  time_t modtime;
}

说明:

  • actime:文件的访问时间
  • modtime:文件的修改时间
  • 文件时间时文件的属性,是和文件数据存储在一起的信息
  • 获取时间函数将存储的信息装载到这个结构体中
  • 实验使用函数utime使用该结构体为文件设置时间属性
  • 头文件:sys/utime.h
目录读取

Linux系统使用dirent结构体存储读取目录的内容

定义如下:

struct dirent{
  long d_ino;
  off_t d_off;
  unsigned short d_reclen;
  unsigned char d_type;
  char d_name[NAME_MAX+1];
};

说明:

  • d_ino:索引节点号
  • d_off:在目录文件中的偏移
  • d_reclen:文件名长度
  • d_type:文件类型
  • d_name:文件名
  • Linux使用目录文件包含目录下文件的名字和指向这些文件的有关信息的指针
  • 从定义看出,该结构体不仅指向目录,还指向目录中具体的文件
  • 结构体中存储的文件信息很少,所以起到的是索引的作用

4.2.3 API介绍

打开目录

Linux系统使用opendir打开一个目录

语法如下:

DIR* opendir(const char* path);

说明:

  • path:目录的路径
  • 返回值:一个DIR结构体指针,打开失败时返回空指针
  • 头文件:sys/stat.hdirent.h
创建目录

Linux系统使用mkdir创建一个目录

语法如下:

int mkdir(const char *path, mode_t mode);

说明:

  • path:要创建的目录的路径
  • mode:目录权限,实验中由stat.st_mode指示
  • 返回值:
    • 0表示返回成功
    • -1表示错误
  • 头文件:sys/stat.hdirent.h
读目录

Linux系统使用readdir函数读取目录内容

语法如下:

struct dirent* readdir(DIR *dir_handle)

说明:

  • dir_handle:一个DIR结构体指针
  • 返回值:一个dirent结构体指针
  • 头文件:dirent.h
时间属性修改

Linux系统使用utime函数为一个文件修改时间属性

语法如下:

int utime(const char *pathname, const struct utimbuf *times);
int lutimes(const char *pathname,const struct timeval *times);

说明:

  • lutes:用于修改软连接文件信息
  • pathname:要修改的文件的路径
  • times:一个utimbuf结构体,存储文件时间属性
  • 返回值:
    • 成功返回0
    • 失败返回-1
  • 头文件:utime.h
获取文件信息

Linux系统使用statlstat函数获取文件相关信息,但是stat不支持软连接文件,所以在实验中使用lstat函数获取文件信息

语法如下:

int lstat(const char *path, struct stat *buf);

说明:

  • path:文件路径名
  • buf:指向stat结构体的指针
  • 调用lstat函数,则将文件对应的属性装载进stat结构体中
  • 返回值:
    • 执行成功返回0
    • 执行失败返回-1
  • 头文件:sys/stat.h
打开文件

Linux系统使用open函数打开一个文件,获得该文件的访问句柄(一个整数)

定义如下:

int fd=open(const char *pathname,int flag);
int fd=open(const char *pathname,int flag,mode_t mode);

说明:

  • pathname:要打开文件的路径名
  • flag:避免系统安全问题使用的旗标,指定文件打开方式。实验中只需要读取源文件内容,所以使用的是O_RDONLY
  • mode:只有在创建文件时才需要用到,指明文件的权限属性
  • 返回值:返回访问该文件的访问句柄,类型为一个整数,若打开失败则返回-1
  • 头文件:sys/types.hsys/stat.hfcntl.h
创建文件

Linux系统使用creat函数创建一个文件或重写一个已经存在的文件

定义如下:

int creat(const char *pathname,mode_t mode);

说明

  • pathname:要创建的文件的路径
  • mode:要创建文件的权限属性,本实验中该属性需要和复制源文件相同
  • 返回值:返回创建该文件的访问句柄,类型为一个整数,若创建失败则返回-1
  • 头文件:fcntl.h
关闭文件

Linux系统在不需要再使用打开的文件时,需要关闭该文件

定义如下:

int close(int fd);

说明:

  • fd:打开文件时返回的文件访问句柄
  • 返回值
    • 成功关闭返回0
    • 失败返回-1
  • 头文件:unistd.h
写文件

Linux系统使用write函数向已经打开的文件中写入数据

定义如下:

ssize_t write(int fd,const void *buf,size_t nbyte);

说明:

  • fd:打开文件时返回的文件访问句柄
  • buf:缓冲区数据块指针
  • nbyte:数据的大小
  • 该函数会把buf所指的内存写入nbyte个字节到fd所指的文件中,文件读写位置也会随之移动
  • 返回值
    • 成功写入则返回写入的字节数,即nbyte
    • 错误返回-1
  • 头文件:unistd.h

读文件

Linux系统使用read函数从已经打开的文件读取文件

定义如下:

ssize_t read(int fd,void *buf,size_t nbyte);

说明:

  • fd:打开文件时返回的文件访问句柄
  • buf:缓冲区数据块指针
  • nbyte:文件读取的大小
  • 该函数会从fd所指向的文件读出nbyte个字节填入buf所指的数据块中
  • 返回值
    • 若成功返回,则返回读出的字节数,可以和nbyte比较,若少了可能读到了文件末尾
    • 若失败,则返回-1
  • 头文件:unistd.h

4.2.4 程序代码

程序代码以附件形式给出,共一个文件mycp.c,如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUFF_SIZE 4096

void copyLink(char *source, char *destination){
    char oldpath[BUFF_SIZE];
    int rest=readlink(source,oldpath,BUFF_SIZE);
    int sy=symlink(oldpath,destination);
    struct stat statbuff;
    lstat(source,&statbuff);
    struct timeval times[2];
    times[0].tv_sec=statbuff.st_atime;
    times[0].tv_usec=0;
    times[1].tv_sec=statbuff.st_mtime;
    times[1].tv_usec=0;
    lutimes(destination,times);
}

void copyFile(char *source, char *destination)
{
    int sourcefile = open(source, O_RDONLY);
    if (sourcefile == -1)
    {
        printf("Can not open file %s\n", source);
        exit(0);
    }
    struct stat statbuff;
    lstat(source, &statbuff);
    int destinationfile = creat(destination, statbuff.st_mode);
    if (destinationfile == -1)
    {
        printf("Can not create file %s\n", destination);
        exit(0);
    }
    int word;
    char buff[BUFF_SIZE];
    while ((word = read(sourcefile, buff, BUFF_SIZE)) > 0)
    {
        if (write(destinationfile, buff, word) != word)
        {
            printf("Write error");
            exit(0);
        }
    }
    struct utimbuf timebuff;
    timebuff.actime = statbuff.st_atime;
    timebuff.modtime = statbuff.st_mtime;
    utime(destination, &timebuff);
    close(sourcefile);
    close(destinationfile);
}

void copyDir(const char *source, const char *destination)
{
    DIR *dir = opendir(source);
    struct dirent *entry;
    char tempsource[2048], tempdestination[2048];
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue;
        }
        if (entry->d_type == 4)		//d_type为4表示是一个目录
        {
            strcpy(tempsource, source);
            strcpy(tempdestination, destination);
            strcat(tempsource, "/");
            strcat(tempdestination, "/");
            strcat(tempsource, entry->d_name);
            strcat(tempdestination, entry->d_name);
            //将源路径和目标路径都复制到temp中
            struct stat statbuff;
            lstat(tempsource, &statbuff);
            mkdir(tempdestination, statbuff.st_mode);
            struct utimbuf timebuff;
            timebuff.actime = statbuff.st_atime;
            timebuff.modtime = statbuff.st_mtime;
            utime(tempdestination, &timebuff);
            copyDir(tempsource, tempdestination); //递归操作
        }
        else if (entry->d_type==10)     //d_type为4表示是一个符号连接
        {
            strcpy(tempsource, source);
            strcpy(tempdestination, destination);
            strcat(tempsource, "/");
            strcat(tempsource, entry->d_name);
            strcat(tempdestination, "/");
            strcat(tempdestination, entry->d_name);
            //将源路径和目标路径都复制到temp中
            copyLink(tempsource,tempdestination);
        }
        else
        {
            strcpy(tempsource, source);
            strcpy(tempdestination, destination);
            strcat(tempsource, "/");
            strcat(tempsource, entry->d_name);
            strcat(tempdestination, "/");
            strcat(tempdestination, entry->d_name);
            //将源路径和目标路径都复制到temp中
            copyFile(tempsource, tempdestination);
        }
    }
}

int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("Please enter VALID parameters\n");
        printf("Like: mycp sourceDIR destinationDIR\n");
    }

    DIR *dir;
    if ((dir = opendir(argv[1])) == NULL)
    {
        printf("Can not find the Source Direct");
        exit(0);
    }
    closedir(dir);
    if ((dir = opendir(argv[2])) == NULL)
    {
        struct stat statbuff;
        struct utimbuf timebuff;
        lstat(argv[1], &statbuff);
        //使用lstat可以支持软连接
        mkdir(argv[2], statbuff.st_mode);
        timebuff.actime = statbuff.st_atime;
        timebuff.modtime = statbuff.st_mtime;
        utime(argv[2], &timebuff);
    }
    copyDir(argv[1],argv[2]);
    printf("Copy is ok");
    return 0;
}

运行方式如下:

$ gcc mycp.c -o mycp
$ ./mycp source destination

4.2.5 实验结果

先创建测试目录和对应的链接文件,运行如下代码

$ mkdir sourcedir
$ touch sourcedir/testfile
$ ln -s sourcedir/testfile sourcedir/testlinkfile
$ mkdir sourcedir/testdir
$ ln -s sourcedir/testdir sourcedir/testlinkdir
$ ls -l sourcedir

结果如下:

文件复制_第4张图片

运行mycp程序,如下:

$ ./mycp sourcedir destinationdir
$ ls -l destinationdir

结果如下:

文件复制_第5张图片

5. 实验收获与体会

  1. 通过本实验,掌握了许多在Windows和Linux下关于文件和目录操作的系统调用哦,了解系统文件系统的工作方式和工作情况
  2. 对获取文件目录信息以及读取文件信息的API有了更加深入的了解
  3. 文件系统是使用计算机信息系统的重要接口。通过使用文件系统的系统调用命令操作文件,以达到对文件系统实现功能的理解和掌握
  4. 对于本实验的所有数据结构和系统调用函数,均在微软文档和Linux文档中进行阅读和深入理解,实验进一步训练自己阅读文档获取函数、数据结构声明的能力。

你可能感兴趣的:(BIT)