zoukankan      html  css  js  c++  java
  • 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

     


    总结:  

  • 相关阅读:
    9.6、Libgdx之罗盘
    9.5、Libgdx加速度计
    9.4、Libgdx简单字符输入
    9.3、Libgdx手势检测
    9.2.2、Libgdx的输入处理之事件处理
    9.2.1、Libgdx的输入处理之轮询
    基于ip的虚拟主机配置——在一台服务器上绑定多个 IP 地址
    解决Nginx出现403 forbidden
    SpringMVC中JSONP的基本使用
    centOS7 tomcat 开机自启 自启动设置
  • 原文地址:https://www.cnblogs.com/yxnrh/p/11908220.html
Copyright © 2011-2022 走看看