Systemtap examples, DISK IO - 6 Monitoring Changes to File Attributes

3 minute read

背景

例子来自inodewatch2.stp 脚本, 该脚本用以监控指定文件的权限属性更改.  
文件通过stat -c '%D %i' 取出DEV major, minor以及inode. 用法与上一片介绍的inodewatch.stp类似.  
  
脚本内容以及注解 :   
[root@db-172-16-3-150 network]# cd /usr/share/systemtap/testsuite/systemtap.examples/io  
[root@db-172-16-3-150 io]# cat inodewatch2.stp  
#!/usr/bin/stap  
  
global ATTR_MODE = 1  
  
probe kernel.function("setattr_copy")!,  // ! 表示如果无法匹配到这个事件则继续匹配下一个事件, 但是不报错.  
      kernel.function("generic_setattr")!,  // , 如果匹配到了则不继续匹配下一个事件, 这样主要针对不同的内核版本做兼容.  
      kernel.function("inode_setattr") {  
  dev_nr = $inode->i_sb->s_dev  // 文件在内核中的表示, 可以通过MKDEV(major, minor)转换.  
  inode_nr = $inode->i_ino  // 文件的inode号  
  
  if (dev_nr == MKDEV($1,$2) # major/minor device  
      && inode_nr == $3  
      && $attr->ia_valid & ATTR_MODE)  //$attr->ia_valid & ATTR_MODE表示当前是改变文件属性的操作.   
    printf ("%s(%d) %s 0x%x/%u %o %d\n",  
      execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_mode, uid())  
}  
// 使用$attr->ia_mode,输出更新后的文件属性.  
// 这几个事件函数的源码参考本文末尾部分, 通过这个源码可以看出, 我们还可以利用这个函数来跟踪文件的其他变更操作, 例如ctime, atime, mtime的变更以及用户和组的变更.  
  
执行输出举例 :   
// 获得文件的dev和inode  
[root@db-172-16-3-150 ~]# stat -c '0x%D %i' /root/anaconda-ks.cfg  
0x821 1062620  
// 跟踪这个文件的权限更改操作  
[root@db-172-16-3-150 io]# stap inodewatch2.stp 0x8 0x21 1062620  
// 使用以下命令更改/root/anaconda-ks.cfg文件权限属性  
[root@db-172-16-3-150 ~]# chmod 555 /root/anaconda-ks.cfg   
[root@db-172-16-3-150 ~]# chmod 666 /root/anaconda-ks.cfg  
// 可以看到stap的输出如下  
chmod(17939) generic_setattr 0x800021/1062620 100555 0  
chmod(17941) generic_setattr 0x800021/1062620 100666 0  
  
接下来我修改一下这个stp文件, 把它变成跟踪文件属主的变更.  
// 修改后的stp文件  
[root@db-172-16-3-150 io]# vi inodewatch2.stp  
#!/usr/bin/stap  
  
global ATTR_UID = 2  // 参考头文件fs.h  
global ATTR_GID = 4  // 参考头文件fs.h  
  
probe kernel.function("setattr_copy")!,  
      kernel.function("generic_setattr")!,  
      kernel.function("inode_setattr") {  
  dev_nr = $inode->i_sb->s_dev  
  inode_nr = $inode->i_ino  
  
  if (dev_nr == MKDEV($1,$2) # major/minor device  
      && inode_nr == $3)  
  {  
      if ($attr->ia_valid & ATTR_UID)  
        printf ("%s(%d) %s 0x%x/%u %d %d\n",  
        execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_uid, uid())  
      if ($attr->ia_valid & ATTR_GID)  
        printf ("%s(%d) %s 0x%x/%u %d %d\n",  
        execname(), pid(), probefunc(), dev_nr, inode_nr, $attr->ia_gid, uid())  
  }  
}  
// 执行这个脚本, 同样跟踪anaconda-ks.cfg文件  
[root@db-172-16-3-150 io]# stap inodewatch2.stp 0x8 0x21 1062620  
// 修改anaconda-ks.cfg文件的用户和组  
[root@db-172-16-3-150 ~]# chown pg93:oinstall anaconda-ks.cfg   
[root@db-172-16-3-150 ~]# id pg93  
uid=500(pg93) gid=500(pg93) groups=500(pg93),503(oinstall),504(dba)  
// 可以看到跟踪到的修改, 如下 :   
chown(18453) generic_setattr 0x800021/1062620 500 0  
chown(18453) generic_setattr 0x800021/1062620 503 0  
  
本文的几个事件函数的源码:   
[root@db-172-16-3-150 io]# stap -L 'kernel.function("setattr_copy")'  
[root@db-172-16-3-150 io]# stap -L 'kernel.function("generic_setattr")'  
kernel.function("generic_setattr@fs/attr.c:139") $inode:struct inode* $attr:struct iattr const*  
[root@db-172-16-3-150 io]# stap -L 'kernel.function("inode_setattr")'  
kernel.function("inode_setattr@fs/attr.c:170") $inode:struct inode* $attr:struct iattr*  
  
[root@db-172-16-3-150 io]# less /usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/fs/attr.c  
/**  
 * generic_setattr - copy simple metadata updates into the generic inode  
 * @inode:      the inode to be updated  
 * @attr:       the new attributes  
 *  
 * generic_setattr must be called with i_mutex held.  
 *  
 * generic_setattr updates the inode's metadata with that specified  
 * in attr. Noticably missing is inode size update, which is more complex  
 * as it requires pagecache updates.  
 *  
 * The inode is not marked as dirty after this operation. The rationale is  
 * that for "simple" filesystems, the struct inode is the inode storage.  
 * The caller is free to mark the inode dirty afterwards if needed.  
 */  
void generic_setattr(struct inode *inode, const struct iattr *attr)  
{  
        unsigned int ia_valid = attr->ia_valid;  
  
        if (ia_valid & ATTR_UID)  
                inode->i_uid = attr->ia_uid;  
        if (ia_valid & ATTR_GID)  
                inode->i_gid = attr->ia_gid;  
        if (ia_valid & ATTR_ATIME)  
                inode->i_atime = timespec_trunc(attr->ia_atime,  
                                                inode->i_sb->s_time_gran);  
        if (ia_valid & ATTR_MTIME)  
                inode->i_mtime = timespec_trunc(attr->ia_mtime,  
                                                inode->i_sb->s_time_gran);  
        if (ia_valid & ATTR_CTIME)  
                inode->i_ctime = timespec_trunc(attr->ia_ctime,  
                                                inode->i_sb->s_time_gran);  
        if (ia_valid & ATTR_MODE) {  
                umode_t mode = attr->ia_mode;  
  
                if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))  
                        mode &= ~S_ISGID;  
                inode->i_mode = mode;  
        }  
}  
EXPORT_SYMBOL(generic_setattr);  
  
/*  
 * note this function is deprecated, the new truncate sequence should be  
 * used instead -- see eg. simple_setsize, generic_setattr.  
 */  
int inode_setattr(struct inode *inode, struct iattr *attr)  
{  
        unsigned int ia_valid = attr->ia_valid;  
  
        if (ia_valid & ATTR_SIZE &&  
            attr->ia_size != i_size_read(inode)) {  
                int error;  
  
                error = vmtruncate(inode, attr->ia_size);  
                if (error)  
                        return error;  
        }  
  
        generic_setattr(inode, attr);  
  
        mark_inode_dirty(inode);  
  
        return 0;  
}  
EXPORT_SYMBOL(inode_setattr);  
头文件 :   
/usr/src/debug/kernel-2.6.32-358.el6/linux-2.6.32-358.el6.x86_64/include/linux/fs.h  
/*  
 * Attribute flags.  These should be or-ed together to figure out what  
 * has been changed!  
 */  
#define ATTR_MODE       (1 << 0)  
#define ATTR_UID        (1 << 1)  
#define ATTR_GID        (1 << 2)  
#define ATTR_SIZE       (1 << 3)  
#define ATTR_ATIME      (1 << 4)  
#define ATTR_MTIME      (1 << 5)  
#define ATTR_CTIME      (1 << 6)  
#define ATTR_ATIME_SET  (1 << 7)  
#define ATTR_MTIME_SET  (1 << 8)  
#define ATTR_FORCE      (1 << 9) /* Not a change, but a change it */  
#define ATTR_ATTR_FLAG  (1 << 10)  
#define ATTR_KILL_SUID  (1 << 11)  
#define ATTR_KILL_SGID  (1 << 12)  
#define ATTR_FILE       (1 << 13)  
#define ATTR_KILL_PRIV  (1 << 14)  
#define ATTR_OPEN       (1 << 15) /* Truncating from open(O_TRUNC) */  
#define ATTR_TIMES_SET  (1 << 16)  

参考

1. https://sourceware.org/systemtap/SystemTap_Beginners_Guide/mainsect-disk.html

2. https://sourceware.org/systemtap/examples/

3. /usr/share/systemtap/testsuite/systemtap.examples

4. systemtap-testsuite

5. /usr/share/systemtap/testsuite/systemtap.examples/index.txt

6. /usr/share/systemtap/testsuite/systemtap.examples/keyword-index.txt

7. /usr/share/systemtap/tapset

Flag Counter

digoal’s 大量PostgreSQL文章入口