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>\n.", 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.\n"); 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\n", 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("\n"); 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; }
总结: