3.5.9. Linux内核API之文件系统

3.5.9.1. vfs_fstat

vfs_fstat 根据第一个参数fd查找相应的文件,获取文件的属性信息,然后将属性信息保存在函数的第二个参数stat中

#include <linux/fs.h>
//内核源码kernel/stat.c
int vfs_fstat(unsigned int fd, struct kstat *stat)
  • fd: 文件描述符,与用户态下open打开文件返回值意义相同

  • stat: 用于保存文件的基本信息

vfs_getattr 获取当前虚拟文件系统的属性

#include <linux/fs.h>
//kernel源码fs/stat.c
int vfs_getattr(struct path *path, struct kstat *stat)
  • path: 虚拟文件系统路径

  • stat: 用于保存文件系统信息

struct path {
    struct vfsmount *mnt;
    struct dentry *dentry;
}

vfs_statfs 根据第一个参数dentry获取整个文件系统的一些基本信息,将其保存在第二个参数buf中,此基本信息包括当前文件系统的类型, 文件系统的块数目,文件系统的块大小,文件系统的文件数目,文件系统的文件名长度等信息

#include <linux/fs.h>
//kenel源码fs/statfs.c
int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)

struct kstatfs {
    long f_type;    //文件系统的类型
    long f_bszie;   //文件系统的块大小
    u64  f_blocks;  //文件系统块的数目
    u64  f_bfree;
    u64  f_avail;   //文件系统中可用的块数目
    u64  f_files;   //文件系统中文件的数目
    u64  f_ffree;
    __kernel_fsid_t f_fsid;
    long f_namelen;
    long f_frsize;
    long f_flags;
    long f_spare[4];
};

测试代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/statfs.h>
#include <linux/sched.h>
#include <linux/fs_struct.h>

static int vfs_statfs_init(void)
{
    struct path path;
    struct dentry *dentry = NULL;
    static struct kstatfs buf;
    struct inode *inode;

    dentry = current->fs->pwd.dentry;
    path.dentry = dentry;
    path.mnt  = current->fs->pwd.mnt;
    inode = dentry->d_inode;

    vfs_statfs(&path, &buf);
    printk("f_bsize is :%ld\n", buf.f_bsize);
    printk("f_frsize is :%ld\n", buf.f_frsize);
    printk("f_type is :%ld\n", buf.f_type);
    printk("f_blocks is :%lld\n", buf.f_blocks);
    printk("f_bfree is :%lld\n", buf.f_bfree);
    printk("f_bavail is :%lld\n", buf.f_bavail);
    printk("f_files is :%lld\n", buf.f_files);
    printk("f_ffree is :%lld\n", buf.f_ffree);
    printk("f_fsid is :%ld\n", buf.f_fsid);
    printk("f_namelen is :%ld\n", buf.f_namelen);

    printk("inode->i_sb->s_dev = %d\n", inode->i_sb->s_dev);
    printk("inode->i_ino = %d\n", inode->i_ino);
    printk("inode->i_mode = %d\n", inode->i_mode);
    printk("inode->i_nlink = %d\n", inode->i_nlink);
    printk("inode->i_uid = %d\n", inode->i_uid);
    printk("inode->i_gid = %d\n", inode->i_gid);

    vfs_getattr(&path, &stat);
    printk("stat.dev = %d\n", stat.dev);
    printk("stat.ino = %d\n", stat.ino);
    printk("stat.mode = %d\n", stat.mode);
    printk("stat.nlink = %d\n", stat.nlink);
    printk("stat.uid = %d\n", stat.uid);
    printk("stat.gid = %d\n", stat.gid);

}

static void vfs_statfs_exit(void)
{
    return;
}

module_init(vfs_statfs_init);
module_exit(vfs_statfs_exit);

3.5.9.2. current_umask

current_umask 用来返回当前文件的权限值掩码

掩码

描述

S_IRWXU

文件所有者具有读写执行权限

S_IRUSR

文件所有者具有读权限

S_IWUSR

文件所有者具有写权限

S_IXUSR

文件所有者具有执行权限

S_IRWXG

用户组具有读写执行权限

S_IRGRP

用户组具有读权限

S_IWGRP

用户组具有写权限

S_IXGRP

用户组具有执行权限

S_IRWXO

其他用户具有读写执行权限

S_IROTH

其他所有用户具有读权限

S_IWOTH

其他所有用户具有写权限

S_IXOTH

其他所有用户具有执行权限

#include <linux/fs.h>
//kernel源码fs/fs_struct.c
int current_umask(void)

get_fs_type 用于根据输入参数的名字*name, 获取对应文件系统类型的描述符信息

#include <linux/fs.h>
//kernel源码fs/__filesystems.c
struct file_system_type *get_fs_type(const char *name)

struct file_system_type {
    const char *name;           //文件系统名
    int fs_flags;               //文件系统类型标志

#define FS_REQUIRES_DEV         1
#define FS_BINARY_MOUNTDATA     2
#define FS_HAS_SUBTYPE          4
#define FS_USERNS_MOUNT         8
#define FS_USERNS_DEV_MOUNT     16
#define FS_RENAME_DOES_D_MOVE   32768

    //挂载文件系统的方法
    struct dentry *(*mount)(struct file_system_type *, int, const char *, void *);
    //删除超级块的方法
    void (*kill_sb)(struct super_block *);
    struct module *owner;   //指向实现文件系统的模块的指针
    struct file_system_type *next;  //指向文件系统类型链表下一个元素的指针
    struct list_head fs_supers;     //具有相同文件系统类型的超级块对象链表的头
};

get_max_files 返回在文件系统中可以同时打开的最大文件数目

#include <linux/fs.h>
//kernel源码fs/file_table.c
int get_max_files(void)

get_super 由参数*dev所指定的块设备获取超级块

#include <linux/fs.h>
//kernel源码fs/super.c
struct super_block *get_super(struct block_device *bdev)
  • bdev: 用来指定所获取的是哪个设备上的超级块

struct block_device {
    dev_t   bd_dev;         //block_device的搜索编号
    int     bd_openers;     //打开该设备计数器
    struct inode *bd_inode;
    struct super_block *bd_super;   //块设备文件系统超级块结构体,用来操作块文件系统
    struct mutex    bd_mutex;       //打开/关闭互斥量
    struct list_head bd_inodes;         //设备节点链表
    void *bd_claiming;
    void *bd_holder;
    bool bd_write_holder;
    #ifdef CONFIG_SYSFS
    struct list_head bd_holder_list;
    #endif
    struct block_device *bd_contains;   //含有非分区块结构体
    unsigned bd_block_size;     //用来描述块大小,在申请队列中进行设置
    struct hd_struct *bd_part;  //硬件分区结构
    unsigned bd_part_count; //被打开设备的分区次数及统计信息
    int bd_invalidated;
    struct gendisk *bd_disk;    //虚拟块设备下层的通用磁盘结构
    struct request_queue *bd_queue;
    struct list_head bd_list;
    unsigned long bd_private;
    int bd_fsfreeze_count;
    struct mutex bd_fsfreeze_mutex;
};

struct super_block {
    struct list_head    s_list;                 //指向超级块链表的指针
    dev_t               s_dev;                  //设备标识符
    unsigned char       s_blocksize_bits;       //以位为单位的块大小
    unsigned long       s_blocksize;            //以字节为单位的块大小
    unsigned long long  s_maxbytes;             //文件的最大字节数
    struct file_system_type *s_type;            //文件系统类型
    const struct super_operations *s_ops;       //超级块方法
    struct dquot_operations     *s_qcop;        //磁盘限额处理的方法
    const struct export_operations *s_export_op;//网络文件系统使用的输出操作
    unsigned long       s_flags;                //安装标志
    unsigned long       s_magic;                //文件系统的魔数
    struct dentry       *s_root;                //文件系统根目录的目录项对象
    struct rw_semaphore s_umount;               //卸载所用的信号量
    int                 s_count;                //引用计数器
    atomic_t            s_active;               //次级引用计数器
    #ifdef CONFIG_SECURITY
    void *s_security;                           //指向超级块安全数据结构的指针
    #endif
    struct xattr_handler    **s_xattr;          //指向超级块扩展属性结构的指针
    struct list_head        s_inodes;           //所有索引节点的链表
    struct hlist_head       s_anon;             //用于处理网络文件系统的匿名目录项的链表
    struct list_head        s_mounts;           //
    struct block_device     *s_bdev;
    struct backing_dev_info *s_bdi;
    struct mtd_info         *s_mtd;
    struct hlist_head       s_instance;         //用于给定文件系统类型的超级块对象链表的指针
    unsigned int            s_quota_types;
    struct quota_info       s_dpuot;            //磁盘限额的描述符
    struct sb_writers       s_writers;
    char                    s_id[32];           //包含超级块的块设备名称
    u8                      s_uuid[16];         //UUID
    void                    *s_fs_info;         //指向特定文件系统的超级块信息的指针
    unsigned int            s_max_links;
    fmode_t                 s_mode;
    u32                     s_time_gran;        //时间戳的最小单位
    struct mutex            s_vfs_rename_mutex;
    char                    *s_subtype;
    char                    *s_options;
};

have_submounts 用来检查在parent所指定的目录及其子目录上是否有挂载点

#include <linux/dcache.h>
//kernel源码fs/dcache.c
int have_submounts(struct dentry *parent)

测试代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/path.h>
#include <linux/sched.h>

int get_fs_type_init(void)
{
    const char *name = "ext4";

    struct file_system_type fs = get_fs_type(name);
    printk("the filesystem's name is: %s\n", fs->name);

    int data = get_max_files(); //获取系统中文件数目
    printk("get max files is = %d\n", data);

    struct super_block *sb;
    struct block_device *bdev = current->fs->pwd.dentry->d_sb->s_bdev;     //获取当前文件对应的block_device描述符信息

    sb = get_super(bdev);   //获取bdev所对应的超级块
    printk("the superblock dev number is %d\n", sb->s_dev);

    struct dentry *dentry_parent = current->fs->pwd.dentry->d_parrent;
    data = have_submounts(dentry_parent);
    if(data == 0)
        printk("current dentry has not submount.\n");
    else
        printk("current dentry has submount.\n");

    return 0;
}

void get_fs_type_exit(void)
{
    return;
}

module_init(get_fs_type_init);
module_exit(get_fs_type_exit);

3.5.9.3. inode_add_bytes

inode_add_bytes 增加inode节点的字节数

#include <linux/fs.h>
//kernel源码fs/stat.c
void inode_add_bytes(struct inode *inode, loff_t bytes)

typedef __kernel_loff_t loff_t;
typedef long long __kernel_loff_t;
  • inode: 要被增加字节的inode结构体

  • bytes: 要增加的字节数

struct inode {
    umode_t                     i_mode;     //改变节点的模式
    unsigned short              i_opflags;
    kuid_t                      i_uid;      //所有者标识符
    kgid_t                      i_gid;      //组标识符
    unsigned int                i_flags;    //文件系统的安装标志
    #ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl            *i_acl;
    struct posix_acl            *i_default_acl;
    #endif
    const struct inode_operations *i_op;    //节点的操作函数
    struct super_block          *i_sb;      //指向超级块的指针
    struct address_space        *i_mapping; //指向address_space对象的指针
    #ifdef CONFIG_SECURITY
    void                        *i_security;    //指向索引节点安全结构的指针
    #endif
    unsigned long               i_ino;      //索引节点号
    unioc {
            const unsigned int i_nlink;     //硬链接的数目
            unsigned int __i_nlink;
    };
    dev_t                       i_rdev;     //实设备标识符
    loff_t                      i_size;     //文件的字节数
    struct timespec             i_atime;    //上次访问文件的时间
    struct timespec             i_mtime;    //上次写文件的时间
    struct timespec             i_ctime;    //上次修改索引节点的时间
    spinlock_t                  i_lock;     //保护索引节点一些字段的自旋锁
    unsigned short              i_bytes;    //文件中最后一个块的字节数
    unsigned int                i_blkbits;  //块的位数
    blkcnt_t                    i_blocks;   //文件的块数
    #ifdef __NEED_I_SIZE_ORDERED
    seqcount_t                  i_size_seqcount;
    #endif
    unsigned long               i_state;
    struct mutex                i_mutex;    //节点的信号量
    unsigned long               dirtie_when;
    struct hlist_node           i_hash;     //用于散列表的指针
    struct list_head            i_wb_list;  //dev IO备份列表指针
    struct list_head            i_lru;      //节点的LRU链表
    struct list_head            i_sb_list;  //用于超级块的索引节点链表的指针
    union {
        struct hlist_head       i_dentry;   //引用索引节点的目录项对象链表的头
        struct rcu_head         i_rcu;      //rcu链表的头指针
    };
    u64                         i_version;  //版本号
    atomic_t                    i_count;    //引用计数器
    atomic_t                    i_dio_count;//dio的计数器
    atomic_t                    i_writecount;   //用于写进程的引用计数器
    #ifdef CONFIG_IMA
    atomic_t                    i_readcount;    //读计数器
    #endif
    const struct file_operations *i_fops;
    struct file_lock            *i_flock;   //指向文件锁链表的指针
    struct address_space        i_data;     //文件的address_space对象
    struct list_head            i_devices;  //用于具体的字符和块设备索引节点链表
    union {
        struct pipe_inode_info  *i_pipe;    //如果文件是一个管道,则使用它
        struct block_device     *i_bdev;    //指向块设备驱动程序的指针
        struct cdev             *i_cdev;    //指向字符设备驱动程序的指针
    };
    __u32                       i_generation;
    #ifdef CONFIG_FSNOTIFY
    __u32                       i_fsnotify_mask;    //目录通知事件的位掩码
    struct hlist_head           i_fsnotify_marks;   //位掩码链表头指针
    #endif
    void                        *i_private;     //文件或设备的私有指针
};

inode_get_bytes 用于获取整个inode节点的总字节数

#include <linux/fs.h>
//kernel源码fs/stat.c
loff_t inode_get_bytes(struct inode *inode)
void inode_set_bytes(struct inode *inode, loff_t bytes)
void inode_sub_bytes(struct inode *inode, loff_t bytes)

is_bad_inode 用于判断传入参数inode是否被标记为坏节点

#include <linux/fs.h>
//kernel源码fs/bad_inode.c
int is_bad_inode(struct inode *inode)
void make_bad_inode(struct inode *inode)    //将inode节点标记为坏节点

测试代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/path.h>
#include <linux/sched.h>

int inode_test_init(void)
{
    struct dentry *dentry = current->fs->pwd.dentry;
    struct inode *inode = dentry->d_inode;

    int is_bad = is_bad_inode(inode);
    if(!is_bad) {
        unsigned long inode_len = inode_get_bytes(inode);   //获取inode节点的字节数
        printk("the current inode bytes is %d\n", inode_len);

        inode_add_bytes(inode, 1024);   //给inode节点增加1k
    } else {
        printk("it is a bad inode!\n");
    }

    return 0;
}

void inode_test_exit(void)
{
    return;
}

module_init(inode_test_init);
module_exit(inode_test_exit);

may_umount 用于检查挂载点mnt是否处于忙的状态, 忙的定义为在挂载点上有打开的文件,pwd结构体,或者vfsmount结构体

#include <linux/fs.h>
//kernel源码fs/namespace.c
int may_umount(struct vfsmount *mnt)
int may_umount_tree(struct vfsmount *mnt)