Linux C: 文件操作相关的系统调用

一、常见的文件操作相关的系统调用

普通权限的系统调用 函数 说明
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]);
}

 

Guess you like

Origin blog.csdn.net/superSmart_Dong/article/details/120537114