Linux基础(10)AIO项目设计与POSIX文件操作和目录管理

                          实现fast-cp :拷贝文件到目标对象

Linux的七种文件类型 :https://blog.csdn.net/linkvivi/article/details/79834143

   ls -al :https://www.cnblogs.com/fyc119/p/6959695.html

   文件的属性有三类权限 , 文件拥有者权限 ,  文件拥有者组的权限 , 其他权限 : https://www.cnblogs.com/xiaoxiaoweng/p/10722044.html

          chmod abc file  abc分别表示User、Group、及Other的权限  file表示修改的文件 , 每类都有三种权限 rwx

          r=4,w=2,x=1

          若要rwx属性则4+2+1=7;

          若要rw-属性则4+2=6;

          若要r-x属性则4+1=5。

stat()  https://www.cnblogs.com/Demo1589/p/7845017.html

分三个模块

1.文件操作模块: 也就是POSIX文件操作和目录管理

  1.1 文件状态 :当前文件是否存在

  1.2 文件属性 :权限属性,类型属性...

        文件的类型 :是指电脑为了存储信息而使用的对信息的特殊编码方式,是用于识别内部储存的资料 (设备文件 , 常规文件)

        文件的权限 :读写权限

2.核心IO模式

  2.1发起aio读

  2.2在aio_read的回调函数里发起aio_write(根据偏移量,分段读和写(业务逻辑,生产者和消费者的关系,有读才有写)) , aio_write里sem_post(计时)

3.辅助模块

  程序计时的实现 : 在程序开始时 设置 struct timespec tv1 , tv2; 使用clock_gettime(CLOCK_MONOTONIC , &tv1);获得当前系统时间 , 再在程序结束前再次获取clock_gettime(CLOCK_MONOTONIC , &tv2)结束计时; tv2 - tv1 = 程序的执行时长 , 

          但因为程序里的文件操作(AIO)是非阻塞的, 而且有多个文件(AIO) , 所以要使用同步机制 sem_t blocking_waiter(同步信号量)  , 同步机制: 计时的结束必须要在程序拷贝(AIO)之后完成 , 如: 因为aio是非阻塞的 , 所以在多线程的情况下主线程的计时结束应该在aio的内核线程完成后再结束计时 ,而实现这个同步机制的关键是sem_t blocking_waiter , 在开始计时时  sem_init(&blocking_waiter , 0 ,0) , blocking_waiter 可以看做是一个公共资源 , sem_wait()是阻塞的 它会在blocking_waiter里-1 ,而sem_post() 会在blocking_waiter 里+1只有blocking_waiter >= 0时sem_wait才会返回

sem_t blocking_writer;
sem_init(&blocking_waiter , 0 ,0 );    //第二个参数使用默认0 , 多线程
clock_gettime(CLOCK_MONOTINIC , &tv1);
sem_post(&blocking_waiter);  //在aio的回调函数里使用
sem_wait(&blocking_waiter);  //在主线程里使用
clock_gettime(CLOCK_MONOTINIC , &tv2
struct stat 
{  
   dev_t st_dev;
/* 设备号码 特殊的文件*/ 
    ino_t     st_ino;     /* inode节点号 常规的文件*/
    mode_t    st_mode;    /* 文件对应的模式 , 文件 , 目录等 */
    nlink_t   st_nlink;   /* 文件的连接数*/
    uid_t     st_uid;     /* 文件的所有者 */
    gid_t     st_gid;     /* 文件所有者对应的组 */
    dev_t     st_rdev;    /* 特殊设备号码 */
    off_t     st_size;    /* 普通文件 , 对应文件字节数 */
    blksize_t st_blksize; /* 文件内容对应的块大小 , 比如磁盘的读写单位都是按块计算的*/
    blkcnt_t  st_blocks;  /* 文件内容对应块的数量 */
    time_t    st_atime;   /* 文件最后被访问的时间 */
    time_t    st_mtime;   /* 文件最后被修改的时间 */
    time_t    st_ctime;   /* 文件状态改变的时间 */
};

S_ISLNK(st_mode):是否是一个连接.
S_ISREG(st_mode):是否是一个常规文件.
S_ISDIR(st_mode):是否是一个目录
S_ISCHR(st_mode):是否是一个字符设备.
S_ISBLK(st_mode):是否是一个块设备
S_ISFIFO(st_mode):是否是一个FIFO文件.
S_ISSOCK(st_mode):是否是一个SOCKET文件 

 S_IRUSR:用户读权限

 S_IWUSR:用户写权限

 S_IRGRP:用户组读权限

 S_IWGRP:用户组写权限

 S_IROTH:其他组都权限

 S_IWOTH:其他组写权限

4.实现流程: 

  4.1 辅助模块的使用 , 创建时间对象struct timespec tv1, tv2; 程序开始计时获取系统时间clock_gettime(CLOCK_MONOTONIC, &tv1); 

    初始一个信号量clock_gettime(CLOCK_MONOTONIC, &tv1); , 配合sem_post() , sem_wait()使用 , 当sem_wait() >= 0 时程序才继续运行 把读写操作完成

    才返回,否则阻塞 , 当sem_wait()返回后销毁信号量 , 再次获取系统时间 clock_gettime(CLOCK_MONOTONIC, &tv2); , tv2-tv1 = 程序运行的时长

  4.2 文件操作模块,     获取源文件信息if(stat(src , src_stat) == -1) 失败返回-1并结束程序 , 获取目标信息时if (stat(dst, &dst_stat) == -1) 失败返回-1说明目标不是一个文件 , 可能是个目录或其他的东西. 错误代码存在errorif , if (errno != ENOENT) 如果出错,一定是因为没有输入 , 

    errno != ENOENT :报错不是因为没有这个路径或文件, 是其他的错误那么一定是没有输入目标

    如果输入了判断其是否是一个目录if (S_ISDIR(src_stat.st_mode)) , 如果是一个目录且存在则不用做其他操作直接打开目录opendir(dir);并往里复制源文件

     traverse_dir_copy(src);  如果不存在则创建目录并设置读写执行权限if(mkdir(dst, S_IRWXU | S_IRWXG) == -1)并往里复制源traverse_dir_copy(src); 

    如果if (stat(dst, &dst_stat) == -1) 返回成功,说明目标是一个文件, 直接赋值文件到目标里copy_regular(src, dst);,不存在则创建文件后再复制

  4.3 核心IO模块,  获取源信息if (stat(src_file, &stat_buf) == -1 ),并打开if ((src_fd = open(src_file, O_RDONLY)) < 0) ,因为经过上述的判断, 目标是一个文件

    所以创建并以写入的方式打开 if ((dst_fd = open(dst_file, O_WRONLY| O_CREAT, stat_buf.st_mode)) < 0)并附上和写相同的权限 , 创建之后获取目标的信息

    因为确定目标是一个文件所以可以以if (fstat(dst_fd, &stat_dst) == -1)文件的方式获取 , 判断源和目标的设备号且inode节点号是否一致, 两个一致证明是

    相同文件可以直接退出程序 ,

    posix_fadvise(src_fd, 0, stat_buf.st_size, POSIX_FADV_WILLNEED); 预分配内存 :在做大量拷贝时, 预分配内存会提高读写效率 , 因为预分配的是一块大的

    内存,再从里面分配小块的内存,这些小的内存是连续的 , 而直接malloc的可能是不连续的, 这样就避免了内核分配内存时过于繁琐 , 从而提高了效率

    if (fallocate(dst_fd, 0, 0, stat_buf.st_size) == -1);    分配预定大小的内存 , 之后的malloc都会从分配好的大块内存中拿一块小的给malloc直到分配完

     文件较大时分段拷贝(多开几个AIO)可以有效的提高效率 , 其中按页开始分段(一页4K) , 根据偏移值来分段, 

     获取页大小page_size = getpagesize(); 获取页的数量num_pages = stat_buf.st_size / page_size + 1; +1是预防非整页而多出来的比如4页多出一两个字符

    一次需要读取或者写入的大小buffer_size = page_size; 开始读取和写入for (i = 0; i < stat_buf.st_size; i += buffer_size)

typedef struct handler_context
{
    struct aiocb* m_aiocb;      //需要异步操的对象
    size_t m_offset;            //偏移量
    size_t m_file_size;         //文件大小
    int m_src_fd;               //源文件fd
    int m_dst_fd;               //目标文件fd
} handler_context;

    循环里发起AIO 并 设置分配 handler_context对象 和 aiocb对象

 struct aiocb* r_aiocb = (struct aiocb*)malloc(sizeof(struct aiocb));
        handler_context* r_context = (handler_context *) malloc(sizeof(handler_context));
        bzero ((char *)r_context, sizeof(handler_context));
        bzero ((char *)r_aiocb, sizeof(struct aiocb));
        
        // context to be passed to handler
        r_context->m_aiocb = r_aiocb;
        r_context->m_offset = i;
        r_context->m_file_size = stat_buf.st_size;
        r_context->m_src_fd = src_fd;
        r_context->m_dst_fd = dst_fd;

        // basic setup
        r_aiocb->aio_fildes = src_fd;
        r_aiocb->aio_nbytes = buffer_size;
        r_aiocb->aio_offset = i;
        r_aiocb->aio_buf = buffer_block;

        // thread callback
        r_aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD;
        r_aiocb->aio_sigevent.sigev_notify_function = aio_read_handler;
        r_aiocb->aio_sigevent.sigev_notify_attributes = NULL;
        r_aiocb->aio_sigevent.sigev_value.sival_ptr = (void *)r_context;

    aio_read() 后 aio_read_handler回调函数 里再调用 aio_write_handler回调函数 并 aio_write() ++lock_num_requests; sem_post(&blocking_waiter);

  4.4目录拷贝

    先判断当前目标是否是一个目录 S_ISDIR , 在通过opendir(char *dir)判断当前目录是否存在,能open说明存在 , 直接开始拷贝目录traverse_dir_copy(src) ,

    反之则不存在 ,目录不存在则先创建目录if(mkdir(dst, S_IRWXU | S_IRWXG) == -1)RWX代表权限 ,U代表用户 ,G代表用户组 , 创建好目录后 , 再进行目录的

    拷贝traverse_dir_copy(src) , 目录的拷贝只要做两件事就可以了 , 1.获取目录list (目录和文件都有一个自己的结构体对象,目录的是dir_entry) , 具体的获取

    1.DIR * p = opendir(pdir);获得源目录并创建一个dirent结构体对象 , 2.读取目录while((q=readdir(p))!=NULL), readdir()并不是全部获得只能获取一个 , 比如A目录有B ,C; 第一次只会获得B , 第二次readdir会获得C , 内核会实现这样的机制, 我们只要多次readdir就行了 . 获取之后判断获得的那一个是否带点(.)开头的目录或文件

    Linux里以点开头的文件都叫隐藏文件 , 而点代表当前目录,点点代表上级目录 ,所以点开头if(q->d_name[0] == '.')的文件都continue跳过 , 不需要拷贝

#define DT_UNKNOWN    0    //未知的
#define DT_FIFO       1    //管道
#define DT_CHR        2    //字符设备文件
#define DT_DIR        4    //目录
#define DT_BLK        6    //块设备文件
#define DT_REG        8    //普通文件
#define DT_LNK        10   //链接
#define DT_SOCK       12   //sockt文件
#define DT_WHT        14    


  struct dirent {
      ino_t d_ino; /* inode number 索引节点号*/
      off_t d_off; /* not an offset; see NOTES 在目录文件中的偏移*/
      unsigned short d_reclen; /* length of this record 文件名长*/
      unsigned char d_type; /*type of file; not supported by all filesystem types 文件类型*/
      char d_name[256]; /* filename 文件名,最长255字符*/

  };

  判断当前文件的类型if(q->d_type==DT_REG)如果是一个常规文件则进行常规文件的拷贝 , 如果还是目录else if ( q->d_type==DT_DIR)

  在目标目录里继续创建子目录if(mkdir(dst_dir, S_IRWXU | S_IRWXG) == -1) 再递归调用自己,继续遍历子目录 traverse_dir_copy(my_path); 

具体代码实现:

// TODO arrange in alphabetical order, separated by type
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <aio.h>
#include <semaphore.h>
#include <dirent.h>
#include <errno.h>
#include <time.h>

#define MAX_PATH 256

size_t buffer_size;          //一次需要读取或者写入的大小
uint64_t  page_size;         //文件读取时的页数
int lock_num_requests;
//信号量通知
sem_t blocking_waiter;

//目录文件路径
char * src;
char * dst;

char *tem_buf;

void aio_read_handler (sigval_t  sigval);
void aio_write_handler(sigval_t signal);
char *format_path(char* path) ;
char* Find_Last_dir_Path(char * path);
int is_exist_dir(char* _dir);
void traverse_dir_copy(char * pdir);
char * split_str(char *path,char *src_path,char **result);

//目录或文件需要复制的结构体对象
typedef struct handler_context
{
    struct aiocb* m_aiocb;      //需要异步操的对象
    size_t m_offset;            //偏移量
    size_t m_file_size;         //文件大小
    int m_src_fd;               //源文件fd
    int m_dst_fd;               //目标文件fd
} handler_context;

/*****************************************************************************
 函 数 名  : aio_read_handler
 功能描述  : aio异步读取函数
 输入参数  : sigval_t  sigval  
 输出参数  : 无
 返 回 值  : void
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
void aio_read_handler (sigval_t  sigval)
{
    size_t nbytes;
    size_t w_nbytes = 0;
    handler_context* hctx = (handler_context*)sigval.sival_ptr;
    if (aio_error(hctx->m_aiocb)) 
    {
        perror("read aio error");
        exit(-1);
    }
    nbytes = aio_return(hctx->m_aiocb);
    int i = 0;
    void * buffer = (void *)hctx->m_aiocb->aio_buf;
    /*w_nbytes = pwrite(hctx->m_dst_fd, buffer, nbytes, hctx->m_offset);
    if (w_nbytes != nbytes) {
    perror("sync write error");
    exit(-1);
    }
    sem_post(&blocking_waiter);*/
    // now send an async write request for the destination file
    // init aiocb struct
    struct aiocb*  w_aiocb = (struct aiocb*)malloc(sizeof(struct aiocb));
    handler_context* w_context = (handler_context *) malloc(sizeof(handler_context));
    bzero ((char *)w_context, sizeof(handler_context));
    bzero ((char *)w_aiocb, sizeof(struct aiocb));
    
    // context to be passed to handler
    w_context->m_aiocb = w_aiocb;
    w_context->m_offset = hctx->m_offset;
    w_context->m_file_size = hctx->m_file_size;
    w_context->m_src_fd = hctx->m_src_fd;
    w_context->m_dst_fd = hctx->m_dst_fd;

    // basic setup
    w_aiocb->aio_fildes = hctx->m_dst_fd;
    w_aiocb->aio_nbytes = nbytes;
    w_aiocb->aio_offset = hctx->m_offset;
    w_aiocb->aio_buf = buffer;

    // thread callback
    w_aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD;
    w_aiocb->aio_sigevent.sigev_notify_function = aio_write_handler;
    w_aiocb->aio_sigevent.sigev_notify_attributes = NULL;
    w_aiocb->aio_sigevent.sigev_value.sival_ptr = (void *)w_context;

    if (aio_write(w_aiocb) < 0) 
    {
        perror("aio_write error");
        exit(-1);
    }
    ++lock_num_requests;
    sem_post(&blocking_waiter);
}

/*****************************************************************************
 函 数 名  : aio_write_handler
 功能描述  : aio异步写入函数
 输入参数  : sigval_t  sigval  
 输出参数  : 无
 返 回 值  : void
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
void aio_write_handler (sigval_t sigval)
{
    size_t nbytes;
    handler_context* hctx = (handler_context*)sigval.sival_ptr;
    if (aio_error(hctx->m_aiocb)) 
    {
        perror("write aio error");
        exit(-1);
    }
    nbytes = aio_return(hctx->m_aiocb);
    sem_post(&blocking_waiter);
  //free(hctx->m_aiocb->aio_buf);
}

/*****************************************************************************
 函 数 名  : copy_regular
 功能描述  : aio异步文件复制
 输入参数  : const char* src_file  
             const char* dst_file  
 输出参数  : 无
 返 回 值  : int
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月11日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
int copy_regular (const char* src_file, const char* dst_file)
{
    
    //源文件句柄和目标文件句柄
    int src_fd;
    int dst_fd;

    //内存页数和块内存指针
    uint64_t num_pages;
    void * buffer_block;
    
    // get the page_size for the system
    page_size = getpagesize();
    
    struct stat stat_buf, stat_dst;
    // stat the source file
    if (stat(src_file, &stat_buf) == -1 ) 
    {
        perror("source file stat error");
        exit(-1);
    }
    // if its a directory, create and exit
    //    if (S_ISDIR(stat_buf.st_mode)) 
    //    {
    //        if (mkdir(dst_file, S_IRWXU | S_IRWXG))
    //        {
    //            perror("mkdir error");
    //            exit(-1);
    //        }
    //        return 0;
    //    }
    // open the source file for reading RDONLY
    if ((src_fd = open(src_file, O_RDONLY)) < 0) 
    {
        perror("source file open error");
        exit(-1);
    }
    // open the destination file for writing,没有就创建,并附加上权限
    if ((dst_fd = open(dst_file, O_WRONLY| O_CREAT, stat_buf.st_mode)) < 0) 
    {
        perror("destination file open error");
        exit(-1);
    }
    
    if (fstat(dst_fd, &stat_dst) == -1) 
    {
        perror("fstat destination error");
        exit(-1);
    }
    
    // check if input and output are the same,输入输出是否相同
    if (stat_buf.st_dev  == stat_dst.st_dev && 
        stat_buf.st_ino == stat_dst.st_ino)
        return 0;
  
    // TODO tell the kernel that we will need the input file
    posix_fadvise(src_fd, 0, stat_buf.st_size, POSIX_FADV_WILLNEED);
  
    // more efficient space allocation via fallocate for dst file
    if (fallocate(dst_fd, 0, 0, stat_buf.st_size) == -1) 
        perror("destination file fallocate");
  
    // decide the number of pages in the input file and malloc a buffer accordingly
    num_pages = stat_buf.st_size / page_size + 1;
    buffer_size = page_size; 
    //(num_pages < BUF_MAX) ? (num_pages * page_size) : (BUF_MAX * page_size);

    // now start sending aio read requests
    size_t i;
    for (i = 0; i < stat_buf.st_size; i += buffer_size)
    {
        //posix_fadvise(src_fd, i, buffer_size, POSIX_FADV_SEQUENTIAL);
        buffer_block = (void *)malloc(buffer_size);
        if (errno == ENOMEM) 
        {
            perror("malloc for buffer error");
            exit(-1);
        }  
        // init aiocb struct
        struct aiocb* r_aiocb = (struct aiocb*)malloc(sizeof(struct aiocb));
        handler_context* r_context = (handler_context *) malloc(sizeof(handler_context));
        bzero ((char *)r_context, sizeof(handler_context));
        bzero ((char *)r_aiocb, sizeof(struct aiocb));
        
        // context to be passed to handler
        r_context->m_aiocb = r_aiocb;
        r_context->m_offset = i;
        r_context->m_file_size = stat_buf.st_size;
        r_context->m_src_fd = src_fd;
        r_context->m_dst_fd = dst_fd;

        // basic setup
        r_aiocb->aio_fildes = src_fd;
        r_aiocb->aio_nbytes = buffer_size;
        r_aiocb->aio_offset = i;
        r_aiocb->aio_buf = buffer_block;

        // thread callback
        r_aiocb->aio_sigevent.sigev_notify = SIGEV_THREAD;
        r_aiocb->aio_sigevent.sigev_notify_function = aio_read_handler;
        r_aiocb->aio_sigevent.sigev_notify_attributes = NULL;
        r_aiocb->aio_sigevent.sigev_value.sival_ptr = (void *)r_context;

        if (aio_read(r_aiocb) < 0) 
        {
            perror("aio_read error");
            exit(-1);
        }
        ++lock_num_requests;
    } 
    return 0;
}

/*****************************************************************************
 函 数 名  : main
 功能描述  : 主函数
 输入参数  : int argc       
             char * argv[]  
 输出参数  : 无
 返 回 值  : int
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
int main(int argc, char * argv[])
{
    if (argc != 3)
    {
        printf("usage : %s <source> <destination>
.", argv[0]);
        return 0;
    }
    //时间对象
    struct timespec tv1, tv2;
    lock_num_requests = 0;
    //文件开始复制前的时间
    clock_gettime(CLOCK_MONOTONIC, &tv1);
    //初始化信号量
    sem_init(&blocking_waiter, 0, 0);
    uint64_t i;
    //格式化文件路径
    src = argv[1];
    dst = argv[2];
    
    //获取源文件的属性
    struct stat src_stat, dst_stat;
    if (stat(src, &src_stat) == -1)
    {
        perror("source file stat error");
        exit(-1);
    }
    
    //获得目标文件的属性 ,获取失败说明目标并非是文件可能是目录或其他的
    if (stat(dst, &dst_stat) == -1) 
    {
        // if error, must be because of a no entry
        if (errno != ENOENT)        //报错说明未输入 , 并非是目录不存在
        {
            perror("destination file stat error");
            exit(-1);
        }
        /*
            如果源文件是个目录
        */
        if (S_ISDIR(src_stat.st_mode)) 
        {
            int iRet = is_exist_dir(dst);    //判断目录是否存在
            if ( iRet == 1 )
            {
                printf("the dest alread exist the same directory.
");
                traverse_dir_copy(src);        //进入目录,并拷贝文件到目标
            }
            else
            {
                //S_IRWXU 00700权限,代表该文件所有者拥有读,
                //S_IRWXG 00070权限,代表该文件用户组拥有读,
                //创建一个目录, 所有者和所有组有读
                if(mkdir(dst, S_IRWXU | S_IRWXG) == -1)    //创建目录
                {
                    perror("destination mkdir failed");
                    exit(-1);
                }
                /*遍历并拷贝*/
                traverse_dir_copy(src);        //进入目录,并拷贝文件到目标
            } 
        }
        else  
            copy_regular(src, dst);         //是个文件
    }
    
    for (i = 0; i < lock_num_requests; ++i)     //lock_num_requests信号量里有多少个post
    {
        sem_wait(&blocking_waiter);    //sem_wait对应的信号量,如果blocking_waiter >= 0 sem_wait返回否则一直阻塞
    }
    
    sem_destroy(&blocking_waiter);
    clock_gettime(CLOCK_MONOTONIC, &tv2);
    
    uint64_t tv = (tv2.tv_sec - tv1.tv_sec) * 1000000000+ tv2.tv_nsec -tv1.tv_nsec;
    printf("completion time = %ld.%06ld s
", tv / 1000000000, tv % 1000000000);
    return 0;
}

/*****************************************************************************
 函 数 名  : Find_Last_dir_Path
 功能描述  : 查找原路径中的最后一个目录
 输入参数  : char * path  
 输出参数  : 无
 返 回 值  : char*
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
char* Find_Last_dir_Path(char * path)
{
    char *temp = (char*)malloc(strlen(path)+1);
    strcpy(temp,path);
    int i = 0;
    for(i = strlen(path) - 2; ;i--)
    {
        if(temp[i] == '/')
            break;
    }
    temp[i]=0;
    char *result = (char*)malloc(strlen(&temp[i + 1])+1);
    strcpy(result,&temp[i + 1]);
    result[strlen(result) -1] = 0; 
    free(temp);
    return result;
}

/*****************************************************************************
 函 数 名  : is_exist_dir
 功能描述  : 判断该路径含有的目录是否存在
 输入参数  : char* _dir  
 输出参数  : 无
 返 回 值  : int
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
int is_exist_dir(char* _dir)
{    
    DIR * dir = NULL;    
    dir = opendir(_dir);    
    if(dir == NULL)   
    {        
        closedir(dir); 
        return 0;    
    }    
    else    
    {        
        closedir(dir);       
        return 1;    
    }
}

/*****************************************************************************
 函 数 名  : traverse_dir_copy
 功能描述  : 递归遍历目录并复制
 输入参数  : char * pdir  
 输出参数  : 无
 返 回 值  : void
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年7月23日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
void traverse_dir_copy(char * pdir)
{
    //打开指定目录,并取得一个目录流指针
    DIR * p = opendir(pdir);
    //如果错误,直接返回
    if ( NULL == p )
    {
       perror("opendir is error");
       return;
    }
    //定一个目录结构体对象
    struct dirent *q;
    //定一个文件属性结构体对象
    struct stat s;

    //定义起始目录路径存放变量
    char my_path[MAX_PATH];
    //定义目的目录路径存放变量
    char des_path[MAX_PATH];
    //定义临时目录路径存放变量
    char tmp_path[MAX_PATH];
    strcpy(my_path,pdir);
    strcpy(tmp_path,pdir);

    //定义起始文件路径存放变量
    char file_name[MAX_PATH];
    //定义临时文件路径存放变量
    char tmp_name[MAX_PATH];
    strcpy(file_name,pdir);
    strcpy(tmp_name,pdir);

    //目标目录
    char dst_dir[MAX_PATH];
    char dst_temp[MAX_PATH];
    strcpy(dst_dir,dst);
    strcpy(dst_temp,dst);

    //目标目录存放文件
    char dst_dir_name[MAX_PATH];
    char dst_temp_name[MAX_PATH];
    strcpy(dst_dir_name,dst);
    strcpy(dst_temp_name,dst);
    
    //读取目录
    while((q=readdir(p))!=NULL)
    {
        //遇到'.' '..'直接过滤掉
        if(q->d_name[0] == '.')
            continue;
        //这个地方可以添加其他文件属性,这里就不添加了
        //判断资源属性,为普通文件
        if(q->d_type==DT_REG)
        {
            //sprintf(des_name,"/%s",q->d_name);
            strcat(file_name,q->d_name); 
            //剔除源文件路径
            char *file_adr = (char*)malloc(MAX_PATH);
            split_str(file_name,src,&file_adr); 
            strcat(dst_dir_name,file_adr);
            free(file_adr);
            //有文件就复制
            copy_regular(file_name, dst_dir_name);
            //还原原始的路径
            strcpy(file_name,tmp_name);
            strcpy(dst_dir_name,dst_temp_name);
            
        }
        else if ( q->d_type==DT_DIR)    //判断资源属性,为目录
        {
            sprintf(des_path,"%s/",q->d_name);
            strcat(my_path,des_path);
            char *file_adr = (char*)malloc(MAX_PATH);
            //char * file_adr = split_str(my_path,pdir);
            split_str(my_path,pdir,&file_adr);
            strcat(dst_dir,file_adr);
            free(file_adr);
            //sprintf(dst_path,"/%s",file_adr);
            //递归调用自己,继续遍历子目录
            if(mkdir(dst_dir, S_IRWXU | S_IRWXG) == -1)
            {
                perror("destination mkdir failed");
                exit(-1);
            }

            memset(des_path,0,sizeof(MAX_PATH));
            
            traverse_dir_copy(my_path);
            strcpy(my_path,tmp_path);
            strcpy(dst_dir,dst_temp);
        }
    }
    puts("
");
    closedir(p); 
}

/*****************************************************************************
 函 数 名  : split_str
 功能描述  : 剥离源路径后剩下的文件和目录名就是需要复制新建的
 输入参数  : char *path      
             char *src_path 
             char **result
 输出参数  : 无
 返 回 值  : char *
 调用函数  : 
 被调函数  : 
 
 修改历史      :
  1.日    期   : 2017年8月12日
    作    者   : xiaofeng
    修改内容   : 新生成函数

*****************************************************************************/
char * split_str(char *path,char *src_path,char **result)
{
    int i = 0;
    char *temp = (char*)malloc(strlen(path)+1);
    strcpy(temp,path);
    for(i = 0 ;;i++)
    {
        if(temp[i] != src_path[i])
            break;
    }
    temp[i - 1] = 0;
    strcpy(*result,&temp[i]);
    free(temp);
    return *result;
}
fast-copy

 


总结:  

原文地址:https://www.cnblogs.com/yxnrh/p/11908220.html