attr.c 2007-3-6
inode,即文件,拥有各种属性,时间/从属/size等。内核提供对这些属性改变的通知事件。同一个文件可以有很多的进程注册了notify 每个进程感兴趣的属性也可以不同。这个文件提供了几个接口,来统一对文件属性的修改进行鉴权/实施/以及通知。 struct iattr *attr : 要修改的属性通过这个结构传递,fs.h 定义的 #define ATTR_MODE 1 #define ATTR_UID 2 设置到iattr的ia_valid,来指明要修改的属性,之后的鉴权/修改/通知都通过ia_valid来确定。 相关的代码并不复杂。简单罗列到这里 以供参考。
/* * 就是根据attr,看看你要改什么东西,权限购不够 */ /* POSIX UID/GID verification for setting inode attributes. */ int inode_change_ok(struct inode *inode, struct iattr *attr) { int retval = -EPERM; unsigned int ia_valid = attr->ia_valid;
/* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) goto fine;
/* Make sure a caller can chown. */ if ((ia_valid & ATTR_UID) && (current->fsuid != inode->i_uid || attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN)) goto error;
/* Make sure caller can chgrp. */ if ((ia_valid & ATTR_GID) && (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid) && !capable(CAP_CHOWN)) goto error;
/* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) goto error; /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : inode->i_gid) && !capable(CAP_FSETID)) attr->ia_mode &= ~S_ISGID; }
/* Check for setting the inode time. */ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) { if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) goto error; } fine: retval = 0; error: return retval; }
/* * 根据attr的目的,设置inode的各种属性 * 要先调用inode_change_ok看看权限是否购,然后使用此函数 * 设置相应属性 */ void inode_setattr(struct inode * inode, 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_SIZE) vmtruncate(inode, attr->ia_size); if (ia_valid & ATTR_ATIME) inode->i_atime = attr->ia_atime; if (ia_valid & ATTR_MTIME) inode->i_mtime = attr->ia_mtime; if (ia_valid & ATTR_CTIME) inode->i_ctime = attr->ia_ctime; if (ia_valid & ATTR_MODE) { inode->i_mode = attr->ia_mode; if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) inode->i_mode &= ~S_ISGID; } mark_inode_dirty(inode); }
/* * 根据根该的属性,获得一个detnry notify 的mask, dn_mask * inode->i_dnotify_mask 是进程所关心的事件,dn_mask是实际 * 发生的事件,相与即可得知是否需要通知进程 */ static int setattr_mask(unsigned int ia_valid) { unsigned long dn_mask = 0;
if (ia_valid & ATTR_UID) dn_mask |= DN_ATTRIB; if (ia_valid & ATTR_GID) dn_mask |= DN_ATTRIB; if (ia_valid & ATTR_SIZE) dn_mask |= DN_MODIFY; /* both times implies a utime(s) call */ if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME)) dn_mask |= DN_ATTRIB; else if (ia_valid & ATTR_ATIME) dn_mask |= DN_ACCESS; else if (ia_valid & ATTR_MTIME) dn_mask |= DN_MODIFY; if (ia_valid & ATTR_MODE) dn_mask |= DN_ATTRIB; return dn_mask; }
/* * 根据attr 设置inode的属性,并使用信号量通知相关进程 */ int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; int error; time_t now = CURRENT_TIME; unsigned int ia_valid = attr->ia_valid;
if (!inode) BUG();
attr->ia_ctime = now; if (!(ia_valid & ATTR_ATIME_SET)) attr->ia_atime = now; if (!(ia_valid & ATTR_MTIME_SET)) attr->ia_mtime = now;
lock_kernel(); if (inode->i_op && inode->i_op->setattr) /*ext2 未设置此op*/ error = inode->i_op->setattr(dentry, attr); else { /*所以对ext2来讲执行此公共流程*/ error = inode_change_ok(inode, attr); if (!error) inode_setattr(inode, attr); } unlock_kernel(); if (!error) { unsigned long dn_mask = setattr_mask(ia_valid); if (dn_mask) inode_dir_notify(dentry->d_parent->d_inode, dn_mask); } return error; }
dnotify.c 2007.3.6
这个文件和此话题密切相关,顺便也解决了吧。想要监控一个文件的时候,需要首先打开这个文件,然后通过fcntl注册,就会通过信号接受到这个文件相关属性的通知消息。 为此,struct file中有一个f_owner,其中记录了打开这个文件的进程,此文件的等信息。fcntl的时候就需要设置好这个结构,notify就是据此找到信号应该发给那个进程。 另外,需要在inode上挂一个dnotify_struct的链表,记录所有需要得到通知的file(pid记录于file-〉f_owner).此文件就是提供管理这些信息的函数。 实在没有什么说的,见上图和这个两个简单的函数。
/* * 将file->f_owner 和dn 的关系建立起来 */ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { struct dnotify_struct *dn = NULL; struct dnotify_struct *odn; struct dnotify_struct **prev; struct inode *inode; int turning_off = (arg & ~DN_MULTISHOT) == 0;
if (!turning_off && !dir_notify_enable) return -EINVAL; inode = filp->f_dentry->d_inode; if (!S_ISDIR(inode->i_mode)) return -ENOTDIR; if (!turning_off) { dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); if (dn == NULL) return -ENOMEM; } write_lock(&dn_lock); prev = &inode->i_dnotify; for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev) if (odn->dn_filp == filp) break; if (odn != NULL) { if (turning_off) { *prev = odn->dn_next; redo_inode_mask(inode); dn = odn; goto out_free; } odn->dn_fd = fd; odn->dn_mask |= arg; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; goto out_free; } if (turning_off) goto out; filp->f_owner.pid = current->pid; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; dn->dn_magic = DNOTIFY_MAGIC; dn->dn_mask = arg; dn->dn_fd = fd; dn->dn_filp = filp; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; dn->dn_next = inode->i_dnotify; inode->i_dnotify = dn; out: write_unlock(&dn_lock); return 0; out_free: kmem_cache_free(dn_cache, dn); goto out; }
/* * 向所有监听此inode的进程发送信号 */ void __inode_dir_notify(struct inode *inode, unsigned long event) { struct dnotify_struct * dn; struct dnotify_struct **prev; struct fown_struct * fown; int changed = 0;
write_lock(&dn_lock); prev = &inode->i_dnotify; while ((dn = *prev) != NULL) { if (dn->dn_magic != DNOTIFY_MAGIC) { printk(KERN_ERR "__inode_dir_notify: bad magic " "number in dnotify_struct!\n"); goto out; } if ((dn->dn_mask & event) == 0) { prev = &dn->dn_next; continue; } fown = &dn->dn_filp->f_owner; if (fown->pid) send_sigio(fown, dn->dn_fd, POLL_MSG); if (dn->dn_mask & DN_MULTISHOT) prev = &dn->dn_next; else { *prev = dn->dn_next; changed = 1; kmem_cache_free(dn_cache, dn); } } if (changed) redo_inode_mask(inode); out: write_unlock(&dn_lock); }
|