一、常见的文件操作相关的系统调用
普通权限的系统调用 | 函数 | 说明 |
int access(char *pathname,int mode) | 检查对某个文件的权限 | |
int chdir(const char *path) | * 变更目录 | |
int chmod(char *path,model_t mode) | * 更改某个文件的权限 | |
int chown(char *name,int uid,int gid) | * 更改文件的所有人 | |
int chroot(char *pathname); | 将(逻辑)根目录更改为路径名 | |
char *getcwd(char *buf , int size) | * 获取CWD的绝对路径名 | |
int mkdir(char *pathname,model_t mode) | * 创建目录 | |
int rmdir(char *pathname) | * 移除目录 | |
int link(char *oldpath,char *newpath); | * 将新文件名硬链接到旧文件名 | |
int unlink(char *pathname) | 减少文件的链接数;如果数值变成0则删除文件 | |
int symlink(char *oldpath,char *newpath) | 为文件创建一个符号连接 | |
int readlink(char *path,char *buf,int bufsize) | 读取符号链接文件的内容 | |
int rename(char *oldpath,char *newpath | * 重命名文件 | |
int utime(char *pathname,struct utimebuf *time) | * 更改文件的访问和修改时间 | |
int stat(char *filename,struct stat *buf) | 获取文件的状态信息 | |
int fstat(int filedes,struct stat *buf) | 获取文件的状态信息 | |
int lstat(char *filename,struct stat *buf) | * 获取文件的状态信息 | |
int open(char *filr,int flags,int mode) | * 打开一个文件进行读、写、追加 | |
int close(int fd) | * 关闭打开的 文件描述符 | |
int read(int fd,char buf[],int count) | * 读取打开的 文件描述符 | |
int write(int fd,char buf[],int count) | * 写入打开的 文件描述符 | |
int lseek(int fd,int offset,int whence) | 重新定位文件描述符的读/写偏移量 | |
int dup(int oldfd,int newfd) | 将文件描述符复制到最小可用描述符编号中 | |
int dup2(int oldfd,int newfd) | 先将newfd关闭,再把oldfd赋值到newfd中 | |
int umask(int umask); | 设置文件创建掩码;文件权限为(mask &~umask) | |
需要超级用户权限的系统调用 | int mount(char *specialfile, char *mountDir) | 将文件系统添加到挂载点目录上 |
int umount(char *dir); | 分离挂载的文件系统 | |
int mknod(char * path,int model,int device); | 创建特殊文件 |
二、st_mode 标志
宏定义 | 值(十进制) | 含义 |
S_IFMT | 0170000 | 文件类型位域的位掩码 |
S_IFSOCK | 0140000 | socket套接字 |
S_IFLNK | 0120000 | symbolic link 符号链接 |
S_IFREG | 0100000 | 常规文件 |
S_IFBLK | 0060000 | 块设备 |
S_IFDIR | 0040000 | 目录 |
S_IFCHR | 0020000 | 字符设备 |
S_IFIFO | 0010000 | fifo先进先出 |
S_ISUID | 0004000 | 设置UID位 |
S_ISGID | 0002000 | 设置GID位 |
S_ISVTX | 0001000 | 设置粘滞位(Sticky bit) |
S_IRWXU | 00700 | 当前文件的所有者所有权限 |
S_IRUSR | 00400 | 当前文件的所有者读权限 |
S_IWUSR | 00200 | 当前文件的所有者写权限 |
S_IXUSR | 00100 | 当前文件的所有者执行权限 |
S_IRWXG | 00070 | 当前文件的组所有权限 |
S_IRGRP | 00040 | 当前文件的组读权限 |
S_IWGRP | 00020 | 当前文件的组写权限 |
S_IXGRP | 00010 | 当前文件的组执行权限 |
S_IRWXO | 00007 | 当前文件的其他用户所有权限 |
S_IROTH | 00004 | 当前文件的其他用户读权限 |
S_IWOTH | 00002 | 当前文件的其他用户写权限 |
S_IXOTH | 00001 | 当前文件的其他用户执行权限 |
三、文件状态结构体 stat
struct stat {
dev_t st_dev; //文件的设备编号
ino_t st_ino; //节点
mode_t st_mode; //文件的类型和存取的权限
nlink_t st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1
uid_t st_uid; //用户ID
gid_t st_gid; //组ID
dev_t st_rdev; //(设备类型)若此文件为设备文件,则为其设备编号
off_t st_size; //文件字节数(文件大小)
unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小)
unsigned long st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
读取文件时,可以获取文件的文件属性, 可以用以下三种方法
int stat(const char *file_name ,struct stat *buf) 按文件名获得文件的stat信息,如果时链接文件获取链接文件所指向的文件信息
int fstat(int filedes ,struct stat *buf) 和stat函数效果一样,只不过传入的参数时文件描述符
int lstat(const char *file_name ,struct stat *buf) 按文件名获得文件的stat信息,如果时链接文件获取文件本身的信息
下面展示linux 命令 ls 原理的程序:(不支持通配符)
open () 方法是打开文件,遵循符号链接,但是如果想打开文件内容本身,应调用
int readlink(char * pathname ,char buf[] , int bufsize);
/*************myls.c********************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
struct stat mystat,*sp;
char *t1 = "xwrxwrxwr-------";
char *t2 = "----------------";
int ls_file(char *fname){
struct stat fstat ,*sp;
int r,i;
char ftime[64];
sp =& fstat;
if( (r = lstat(fname,&fstat)) <0 ){
printf("can't stat %s\n",fname);
exit(1);
}
if ( (sp->st_mode &0xF000) == 0x8000 ){
// S_ISREG
printf("%c",'-');
}else if ( (sp->st_mode &0xF000) == 0x4000 ){
// S_ISDIR
printf("%c",'d');
}else if ( (sp->st_mode &0xF000) == 0xA000 ){
// S_ISLNK
printf("%c",'l');
}
for ( i =8 ; i>=0 ;i--){
if (sp->st_mode & (1 << i )){
printf("%c",t1[i]);
} else {
printf("%c",t2[i]);
}
}
printf("%4d ",sp->st_nlink);
printf("%4d ",sp->st_gid);
printf("%4d ",sp->st_uid);
printf("%8d ",sp->st_size);
//print time
strcpy(ftime ,ctime(&sp->st_ctime));
ftime[strlen(ftime) -1 ] =0;
printf ("%s " , ftime);
//print name
printf ("%s",basename(fname));
//if symbolic file , print symfile ->linkname
if( (sp->st_mode &0xF000) ==0xA000){
//uss readlink() to read linkname
char *linkname;
readlink (fname , linkname,1024);
printf(" -> %s" , linkname); //print linked name
}
printf("\n");
}
int ls_dir(char *dname){
struct dirent *ep ;
DIR *dp = opendir(dname);
if(!dp){
printf("no such dir %s\n",dname);
exit(1);
}
char newpath[1024];
while( ep = readdir(dp)){
strcpy(newpath,dname);strcat(newpath , ep ->d_name);
ls_file(newpath);
}
}
int main(int argc , char *argv[]){
struct stat mystat, *sp =&mystat;
int r ;
char *filename , path[1024] ,cwd[256];
filename = "./";
if (argc >1){
filename =argv[1];
}
if( r = lstat(filename,sp) < 0 ){
printf("no such file %s\n",filename);
}
strcpy(path , filename);
if(path[0] != '/'){
//相对路径
getcwd(cwd,256);
strcpy(path,cwd); strcat(path ,"/");strcat(path , filename);
}
if(S_ISDIR(sp -> st_mode)){
printf ("path : %s\n", path);
ls_dir(path);
}
else
ls_file(path);
}
四、复制文件, cp -r [srcPath] [destPath]原理
案例分析:
1)src必须存在,dest如果不存在则需要创建
2)如果src 是一个文件,dest 是一个文件或目录。如果是目录则创建同名文件,如果是文件,则直接复制文件内容
3)如果src是一个目录,那么dest必须一定是一个目录,通过readdir遍历src目录下的文件和字目录,复制到dest中
4)如果src 和dest 是同名文件则不复制 , 如果dest 是src的后代目录则不能复制,包括自身。
可以分3个层级来组织程序:
1)最底层:文件复制文件 cpf2f
2)中间层: 将文件复制到目录中 cpf2d ,判断dest目录存在,然后调用 1 步骤
3) 最高层: 目录复制到目录cp2d2, 通过readdur遍历src目录,如果成员是文件则调用步骤2),如果成员是目录则 递归调用步骤 3)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <fcntl.h>
// for stat syscalls
#include <sys/stat.h>
#include <unistd.h>
// for opendir, readdir syscalls
#include <sys/types.h>
#include <dirent.h>
// cp file to file
int cpf2f(char *src, char *dst)
{
int fd, gd, n, r1, r2, mode;
char buf[1024];
struct stat st1, st2;
r1 = lstat(src, &st1);
if (r1 < 0){
printf("src %s does not exist\n", src);
return -1;
}
if (S_ISDIR(st1.st_mode)){
printf("src %s is not a file\n", src);
return -1;
}
r2 = lstat(dst, &st2);
if (r2 == 0){ // dst exist
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino){
printf("Src %s and dst %s are the same file\n", src, dst);
return -1;
}
}
if (r2 < 0){ // dst not exist; src if a file
mode = st1.st_mode;
if (S_ISLNK(mode)){
printf("%s is a symlink file ==> ", src);
n = readlink(src, buf, 1024); buf[n] = 0;
printf("%s\n", buf);
// make a symbolic file
symlink(buf, dst);
return 0;
}
}
// dst not but src is NOT LNK OR dst exist: cp src to dst
fd = open(src, O_RDONLY);
gd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode);
while( (n=read(fd, buf, 1024)) )
write(gd, buf, n);
close(fd); close(gd);
return 0;
}
int cpf2d(char *f1, char *f2)
{
int n, r1, r2, size;
char buf[1024], name[128], f3[128], temp[128];
DIR *gd;
struct stat st, st3;
struct dirent *ep;
printf("cpf2d: cp %s into %s\n", f1, f2);
r2 = lstat(f2, &st);
if (r2 < 0 || S_ISDIR(st.st_mode)==0){
printf("no such dir %s\n", f2);
return -1;
}
strcpy(f3, f2);
strcat(f3, "/");
strcat(f3, basename(f1));
if (lstat(f3, &st) < 0){ // f2/basename(f1) does not exist
return cpf2f(f1, f3);
}
// f2/basename(f1) exists in f2/
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
return cpf2f(f1, f3);
else{
printf("cpf2d but f1 is a DIR, can't be true\n");
return cpf2d(f1, f3);
}
}
int sameFile(char *f1, char *f2)
{
struct stat st1, st2;
stat(f1, &st1); stat(f2, &st2);
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
return 1;
return 0;
}
int checkdir(char *f1, char *f2)
{
while(!sameFile(f1, "/")){
if (sameFile(f1, f2))
return 1;
strcat(f2, "/..");
}
return 0;
}
//int checkDir(char *f1, char *f2)
//{
// char temp[128], t[128];
// int r0, r1, r2;
// struct stat st0, st1, st2;
// //printf("checkdir: %s %s\n", f1, f2);
//
// r0 = stat("/", &st0);
printf("r0=%d root DIR = (%x %ld)\n", r0, (int)st0.st_dev, (long)st0.st_ino);
//
// r1 = stat(f1, &st1);
//
// //printf("r1=%d st1=[%x %ld]\n", r1, (int)st1.st_dev, (long)st1.st_ino);
//
// strcpy(temp, f2); strcat(temp, "/..");
// while (1){
// //printf("%s ", temp);
//
// r2 = stat(temp, &st2);
// //printf("r2=%d st2=[%x %ld]\n", r2, (int)st2.st_dev, (long)st2.st_ino);
//
// if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino){
// //printf("found a match\n");
// return 1;
// }
//
// if (st2.st_dev == st0.st_dev && st2.st_ino == st0.st_ino){
// //printf("reached /\n");
// break;
// }
// strcat(temp, "/..");
// }
// return 0;
//}
int cpdir(char *f1, char *f2){
int n, r1, r2, found;
char buf[1024], name[128], dname[128], temp[128];
char src[128], dst[128], f3[128];
DIR *fd, *gd;
struct dirent *ep;
struct stat st1, st2, st3;
fd = opendir(f1);
while( (ep = readdir(fd)) ){
if (strcmp(ep->d_name, ".")==0 || strcmp(ep->d_name, "..")==0)
continue;
strcpy(src, f1); strcat(src, "/");
strcat(src, ep->d_name);
strcpy(dst, f2); strcat(dst, "/"); strcat(dst, ep->d_name);
r1 = lstat(src, &st1);
if (S_ISREG(st1.st_mode) || S_ISLNK(st1.st_mode)){
printf("cpf2f: %s to %s\n", src, dst);
cpf2d(src, f2);
}
if (S_ISDIR(st1.st_mode)){
/******
r = stat(dst, &st3);
if (r<0)
mkdir(dst, 0755);
*******/
//printf("recursive cp dir %s to %s\n", src, dst);
cpd2d(src, dst);
}
}
closedir(fd);
return 1;
}
// recursively cp dir into dir
int cpd2d(char *f1, char *f2)
{
int n, r1, r2, found;
char buf[1024], name[128], dname[128], temp[128];
char temp1[128], temp2[128];
char src[128], dst[128], f3[128];
DIR *fd, *gd;
struct dirent *ep;
struct stat st1, st2, st3;
//printf("entering cpd2d : %s %s\n", f1, f2);
printf("cpd2d: %s %s\n", f1, f2);
// 1. if f1 not exist => error out
r1 = lstat(f1, &st1);
if (r1 < 0 || S_ISDIR(st1.st_mode)==0){
printf("%s is not a dir\n", f1);
return -1;
}
// check f2:
r2 = lstat(f2, &st2);
// 2. if f2 not exist => mkdir f2
if (r2 < 0){ // f2 not exist
printf("mkdir: %s\n", f2);
r2 = mkdir(f2, 0755);
if (r2<0){
printf("DIR %s already exists\n", f2);
}
return cpdir(f1, f2);
}
// f2 exist case:
r2 = stat(f2, &st2);
// 3. f2 existed but NOT DIR ==> error out
if (r2 >= 0 && S_ISDIR(st2.st_mode)==0){
printf("%s is not a dir\n", f2);
return -1;
}
// 4. if f1 and f2 are SAME ==> error out
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino){
printf("can't cp DIR f1 to itself\n");
return -1;
}
// 5. check f2 is NOT under f1: if so error out
// from f2, stat f2/.., f2/../../ until /; check whether ANY of these is f1
if (checkdir(f1, f2)){
printf("%s is inside %s\n", f2, f1);
return -1;
}
// f2 existed:
if (r2 >=0 ){ // f2 exist and is DIR: check whether same as f1 basename
strcpy(temp1, f1);
strcpy(temp2, f2);
if (strcmp(basename(temp1), basename(temp2))){ // NOT same name
strcpy(temp, f2); strcat(temp, "/");
strcpy(temp1, f1);
strcat(temp, basename(temp1)); // create DIR f2/basenem(f1)
printf("mkdir: %s\n", temp);
r2 = mkdir(temp, 0755);
}
printf("mkdir2 DIR %s already exists\n", temp);
return cpdir(f1, temp);
}
// f2 exist but NOT same as basename of f1
return cpdir(f1, f2);
}
int myrcp(char *f1, char *f2)
{
struct stat st1, st2;
int r1, r2, m1, m2, c;
// MUST use lstat() because f1 may be a symlink file
r1 = lstat(f1, &st1);
if (r1 < 0){
printf("no src file %s\n", f1);
return -1;
}
m1 = st1.st_mode;
if (!S_ISREG(m1) && !S_ISDIR(m1) && !S_ISLNK(m1)){
printf("src is not REG, DIR or LNK file\n");
exit(2);
}
r2 = lstat(f2, &st2);
m2 = st2.st_mode;
if (r2 < 0){ // f2 does NOT exist
if (S_ISREG(st1.st_mode) || S_ISLNK(st1.st_mode))
return cpf2f(f1, f2);
if (S_ISDIR(st1.st_mode))
return cpd2d(f1, f2);
}
if (r2==0){ // f2 exists
if (!S_ISREG(m1) && !S_ISDIR(m1) && !S_ISLNK(m1)){
printf("dst %s is not REG, DIR or LNK file\n", f2);
exit(2);
}
}
if (r2 == 0){ // f2 exists; check whether f1 == f2
if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino){
printf("%s and %s are the same file\n", f1, f2);
return -1;
}
}
if (S_ISREG(m1) || S_ISLNK(m1)){
if (S_ISREG(m2))
return cpf2f(f1, f2);
if (S_ISDIR(m2))
return cpf2d(f1,f2);
}
printf("%s is not a file; try dir\n", f1);
if (S_ISDIR(m1)){
if (S_ISREG(m2)){
printf("can't cp dir into file\n");
return -1;
}
r2 = stat(f2, &st2);
if (r2 < 0) // f2 does not exist yet
mkdir(f2, 0755);
r2 = stat(f2, &st2);
m2 = st2.st_mode;
if (S_ISDIR(m2)){
printf("cp dir to dir\n");
return cpd2d(f1, f2);
}
}
}
int main(int argc, char *argv[])
{
if (argc < 3){
printf("Usage: rcp SRC DST\n");
exit(1);
}
return myrcp(argv[1], argv[2]);
}