APUE学习笔记--文件系统(stat、fstat、lstat、空洞文件、读取目录内容、实现自己的du)

  • stat、fstat、lstat

    • 函数定义

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <unistd.h>

      int stat(const char *path, struct stat *buf);
      int fstat(int fd, struct stat *buf);
      int lstat(const char *path, struct stat *buf);

    • 功能描述
      • 这三个函数都通过buf返回一个包含文件详细信息的结构体。
      • 这些文件本身不需要权限,但是这些函数必须拥有在path指向的文件所在的文件夹下的执行权限。
      • stat:通过文件路径获取属性。面对符号链接时,获取的是符号链接指向的文件的属性。
      • fstat:同stat,只是通过文件描述符获取属性。
      • lstat:面对符号链接时,获取的是符号链接的属性。
    • 参数解析
      • struct stat 结构体:

        struct stat {
        dev_t st_dev; /* 包含改文件的设备id号 /
        ino_t st_ino; /
        i节点号 /
        mode_t st_mode; /
        protection /
        nlink_t st_nlink; /
        number of hard links /
        uid_t st_uid; /
        user ID of owner /
        gid_t st_gid; /
        group ID of owner /
        dev_t st_rdev; /
        device ID (if special file) /设备id号
        off_t st_size; /
        total size, in bytes /
        blksize_t st_blksize; /
        blocksize for file system I/O /文件系统IO的块大小
        blkcnt_t st_blocks; /
        number of 512B blocks allocated /块数
        time_t st_atime; /
        time of last access /最后一次访问时间
        time_t st_mtime; /
        time of last modification /最后一次修改文件内容时间
        time_t st_ctime; /
        time of last status change */最后一次修改inode 信息的时间。(i.e., owner, group, link count, mode, etc.).

        };

      • st_mode
        st_mode是一个十六位的整形数,以位图的方式存储了文件类型、文件权限、特殊权限位。

        • 文件类型:
          • d: 目录文件
          • c:字符设备文件
          • b: 二进制文件
          • -: 常规文件
          • l: 符号链接文件
          • s: 网络socket文件
          • p: 匿名管道文件
        • 文件权限(也就是三类用户的rwx权限)

        • 判断一个文件的类型有两种办法:

          • 利用宏定义的函数判断。为真返回1;为假返回0。
            S_ISREG(m) //is it a regular file?

            S_ISDIR(m) //directory?

            S_ISCHR(m) //character device?

            S_ISBLK(m) //block device?

            S_ISFIFO(m)//FIFO (named pipe)?
            S_ISLNK(m) //symbolic link? (Not in POSIX.1-1996.)

            S_ISSOCK(m)//socket? (Not in POSIX.1-1996.)

          • 利用位运算,得到结果。
            S_IFMT 0170000 //bit mask for the file type bit fields
            S_IFSOCK 0140000 // socket
            S_IFLNK 0120000 //symbolic link
            S_IFREG 0100000 // regular file
            S_IFBLK 0060000 //block device
            S_IFDIR 0040000 //directory
            S_IFCHR 0020000 //character device
            S_IFIFO 0010000 //FIFO
            S_ISUID 0004000 //set UID bit
            S_ISGID 0002000 //set-group-ID bit (see below)
            S_ISVTX 0001000 //sticky bit (see below)
            S_IRWXU 00700 //mask for file owner permissions
            S_IRUSR 00400 //owner has read permission
            S_IWUSR 00200 //owner has write permission
            S_IXUSR 00100 //owner has execute permission
            S_IRWXG 00070 //mask for group permissions
            S_IRGRP 00040 // group has read permission
            S_IWGRP 00020 //group has write permission
            S_IXGRP 00010 //group has execute permission
            S_IRWXO 00007 // mask for permissions for others (not in group)
            S_IROTH 00004 //others have read permission
            S_IWOTH 00002 // others have write permission
            S_IXOTH 00001 //others have execute permission
            如上所示,可以利用bit mask等得到想要的文件类型信息、文件权限信息、特殊位信息。(见下文中的例子。)

      • 返回值
        • 正常返回0,失败返回-1,并设置对应的errno。
        • 通过buf返回该文件的详细信息。
      • 例子
#include <sys/types.h>
       #include <sys/stat.h>
       #include <time.h>
       #include <stdio.h>
       #include <stdlib.h>

       int
       main(int argc, char *argv[])
       {
           struct stat sb;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           if (stat(argv[1], &sb) == -1) {
               perror("stat");
               exit(EXIT_SUCCESS);
           }

           printf("File type:                ");

           switch (sb.st_mode & S_IFMT) {
             case S_IFBLK:  printf("block device\n");            break;
           case S_IFCHR:  printf("character device\n");        break;
           case S_IFDIR:  printf("directory\n");               break;
           case S_IFIFO:  printf("FIFO/pipe\n");               break;
           case S_IFLNK:  printf("symlink\n");                 break;
           case S_IFREG:  printf("regular file\n");            break;
           case S_IFSOCK: printf("socket\n");                  break;
           default:       printf("unknown?\n");                break;
           }

           printf("I-node number:            %ld\n", (long) sb.st_ino);

           printf("Mode:                     %lo (octal)\n",
                   (unsigned long) sb.st_mode);

           printf("Link count:               %ld\n", (long) sb.st_nlink);
           printf("Ownership:                UID=%ld   GID=%ld\n",
                   (long) sb.st_uid, (long) sb.st_gid);

           printf("Preferred I/O block size: %ld bytes\n",
                   (long) sb.st_blksize);
           printf("File size:                %lld bytes\n",
                   (long long) sb.st_size);
           printf("Blocks allocated:         %lld\n",
                   (long long) sb.st_blocks);

           printf("Last status change:       %s", ctime(&sb.st_ctime));
           printf("Last file access:         %s", ctime(&sb.st_atime));
           printf("Last file modification:   %s", ctime(&sb.st_mtime));

           exit(EXIT_SUCCESS);
       }

  • 空洞文件

    空洞文件介绍.
    我们可以利用lseek构造一个包含空洞的文件。原本想构造5G的,结果我用的系统,long类型也是32位,只好改为5MB。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char ** argv)
{
	int fd;
	printf("long SIZE :%d\n",sizeof(long));
	if(argc<2)
	{
		fprintf(stderr,"Usage....\n");
		exit(EXIT_FAILURE);
	}
	
	fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(fd==-1)
	{
		perror("open failed!\n");
		exit(EXIT_FAILURE);
	}
	if(lseek(fd,5LL*1024LL*1024LL-1LL,SEEK_SET)==-1)
	{
		perror("lseek()");
		exit(EXIT_FAILURE);
	}
	if(write(fd,"",1)==-1)
	{
		perror("write()");
		exit(EXIT_FAILURE);
	}
	exit(EXIT_SUCCESS);
}

执行后得到一个空洞文件。
在这里插入图片描述
从上图可以注意到如下几点:
1. 文件size为5242800B,而块数只有8,也就是文件真实分配的磁盘大小只有4KB。
2. 使用cp命令后,得到的备份文件,磁盘块数为0。

  • 分析目录/读取目录内容

    • 方法一:glob
      • 函数定义

        #include <glob.h>

        int glob(const char *pattern, int flags,
        int (*errfunc) (const char *epath, int eerrno),
        glob_t *pglob);

        void globfree(glob_t *pglob);//释放glob申请的内存

      • 函数功能
        • 与pattern模式匹配符合的所有文件都存放在pglob中。
        • flags指定了多种执行模式。不指定模式,就设置为0。
        • errfunc用于获得函数执行出错的具体信息。不需要出错信息时,设置为NULL。
        • pglob用于存储模式匹配得到的信息。
          • typedef struct {
            size_t gl_pathc; /* Count of paths matched so far */
            char *gl_pathv; / List of matched pathnames. /
            size_t gl_offs; /
            Slots to reserve in gl_pathv. */
            } glob_t;
      • 注意事项
        • gl_pathv与main函数中的argv有相似之处。如argc为1时,则argv[1]==NULL。所以可用pglob.gl_pathv[i]==NULL判别读取完毕。
      • 返回值
        • 执行成功返回0。否则有如下情况:
          GLOB_NOSPACE
          // for running out of memory,

          GLOB_ABORTED
          //for a read error, and

          GLOB_NOMATCH
          // for no found matches.

      • 例子
#include <stdlib.h>
#include <stdio.h>
#include <glob.h>


#define PAT "*.c"

int main(int argc,char ** argv)
{
	glob_t pglob;
	int i;
	if(glob(PAT,0,NULL,&pglob))
	{
		fprintf(stderr,"glob error\n");
		exit(EXIT_FAILURE);
	}
	for(i=0;pglob.gl_pathv[i];i++)
	{
		puts(pglob.gl_pathv[i]);
	}
	exit(EXIT_SUCCESS);
}

  • 方法二:通过opendir、readdir、closedir(在man手册的第三章)
    • opendir:

      • DIR *opendir(const char *name);
        DIR *fdopendir(int fd);
        • opendir通过目录名字打开一个目录流,返回一个目录流指针。指针指向目录流的起始位置。
        • fopendir与opendir一样,只是是从一个已经打开的文件描述符读取信息。
        • 执行成功返回目录流,否则返回NULL,并设置errno。
    • readdir

      • #include <dirent.h>

        struct dirent *readdir(DIR *dirp);

        int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);

      • readdir通过目录流drip返回一个dirent结构体指针,指向下一个目录项位置。

      • 当读到目录流结束位置或者出现错误,返回NULL。出现错误时,设置errno。

      • dirent的定义如下:
        struct dirent {
        ino_t d_ino; /* inode number /
        off_t d_off; /
        offset to the next dirent /
        unsigned short d_reclen; /
        length of this record /
        unsigned char d_type; /
        type of file; not supported
        by all file system types /
        char d_name[256]; /
        filename */
        };

    • closedir

      • #include <sys/types.h>

        #include <dirent.h>

        int closedir(DIR *dirp);

      • 关闭drip对应的目录流。执行成功的话,也会关闭目录流drip所使用的的文件描述符fd。

      • 执行成功返回0;否则返回-1,并设置errno。

    • 例子

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#define PAT "."
int main()
{
	DIR * tmp;
	struct dirent * cur;
	if((tmp=opendir(PAT))==NULL)
	{
		perror("opendir()");
		exit(EXIT_FAILURE);
	}
	
	while((cur=readdir(tmp)) != NULL)
	{
		puts(cur->d_name);
	}
	closedir(tmp);	
	exit(EXIT_SUCCESS);
}

  • 实现自己的du

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <glob.h>


#define PATHSIZE 1024
//排除每个目录中存在的 . 和 .. 目录文件
static int path_noloop(const char * path)
{
	char * pos;
	pos = strrchr(path,'/');//寻找最后一个'/'
	if(pos == NULL)
		exit(1);	
	if(strcmp(pos+1,".") == 0 || strcmp(pos+1,"..")==0 )	
		return 0;
	return 1;
}

static int64_t mydu(const char * path)
{
	char pathname[PATHSIZE];
	struct stat buf;
	glob_t pglob;
	int64_t sum;
	int i;
	int flag;
	
	if(lstat(path,&buf) == -1)
	{
		perror("lstat()");
		exit(EXIT_FAILURE);
	}
	
	if(!S_ISDIR(buf.st_mode))
		return buf.st_blocks;
	else
	{
		strncpy(pathname,path,PATHSIZE);
		strncat(pathname,"/*",PATHSIZE);
		flag = glob(pathname,0,NULL,&pglob);
		if(flag == GLOB_NOSPACE || flag  == GLOB_ABORTED)
		{
			fprintf(stderr,"/* glob err!");
			exit(EXIT_FAILURE);
		}

		strncpy(pathname,path,PATHSIZE);
		strncat(pathname,"/.*",PATHSIZE);
		flag = glob(pathname,GLOB_APPEND,NULL,&pglob);//这里使用追加模式。把隐藏文件名加入pglob中。
		if(flag == GLOB_NOSPACE || flag == GLOB_ABORTED)
		{
			fprintf(stderr,"/.* glob err!\n");
			exit(EXIT_FAILURE);
		}
		
		sum= buf.st_blocks;//目录文件本身的大小也得加上
		for(i = 0;i<pglob.gl_pathc;i++)
		{
			if(path_noloop(pglob.gl_pathv[i]))
				sum += mydu(pglob.gl_pathv[i]);
		}	
		
		printf("%-8lld   %s\n",sum/2,path);
	}
	globfree(pglob);//一定要记得释放glob申请的堆空间
	return sum;
}

int main(int argc,char ** argv)
{
	if(argc!=2)
	{
		fprintf(stderr,"Usage------\n");
		exit(EXIT_FAILURE);
	}
	printf("%lld\n",mydu(argv[1])/2);
	exit(EXIT_SUCCESS);
}

  • 注意事项
    • 注意这里使用的int64_t类型,也就是long long 类型。
    • 递归的规程中,必须排除掉. 和 … 的干扰。
    • strrchr函数的使用
    • 递归函数能进行一些优化。
      • pathname、buf、flag都只在递归前使用,因此可以把他们定义为static,减少每次递归时,栈空间的消耗。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值