📄 027_fs_attr_c_dnotify_c.html
字号:
<html lang="zh-CN" xmlns:gdoc=""> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <style type="text/css">/* default css */table { font-size: 1em; line-height: inherit;}div, address, ol, ul, li, option, select { margin-top: 0px; margin-bottom: 0px;}p { margin: 0px;}body { margin: 0px; padding: 0px; font-family: Verdana, sans-serif; font-size: 10pt; background-color: #ffffff;}h6 { font-size: 10pt }h5 { font-size: 11pt }h4 { font-size: 12pt }h3 { font-size: 13pt }h2 { font-size: 14pt }h1 { font-size: 16pt }blockquote {padding: 10px; border: 1px #DDD dashed }a img {border: 0}div.google_header, div.google_footer { position: relative; margin-top: 1em; margin-bottom: 1em;}/* end default css */ /* default print css */ @media print { body { padding: 0; margin: 0; } div.google_header, div.google_footer { display: block; min-height: 0; border: none; } div.google_header { flow: static(header); } /* used to insert page numbers */ div.google_header::before, div.google_footer::before { position: absolute; top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_10c6h497:6"> <div align=center>
<table align=center border=0 cellpadding=0 cellspacing=0 height=5716 width=802>
<tbody>
<tr>
<td height=5716 valign=top width=802>
<div align=center>
<pre><b><font color=#ff00ff><font size=4>attr.c</font> <br></font> </b>2007-3-6 </pre>
</div>
<pre> inode,即文件,<span lang=zh-cn>拥有各种属性,时间/从属/size等。内核提供对这些属性改变的通知事件。同一个文件可以有很多的进程注册了notify<br>每个进程感兴趣的属性也可以不同。这个文件提供了几个接口,来统一对文件属性的修改进行鉴权/实施/以及通知。<br> </span>struct iattr *attr : 要修改的属性通过这个结构传递,fs.h 定义的<br> #define ATTR_MODE 1<br> #define ATTR_UID 2<br>设置到iattr的ia_valid,来指明要修改的属性,之后的鉴权/修改/通知都通过ia_valid来确定。 相关的代码并不复杂。简单罗列到这里<br>以供参考。<br></pre>
<pre><font color=#0000ff>/*<br> * 就是根据attr,看看你要改什么东西,权限购不够<br> */</font>
/* 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;</pre>
<pre> <font color=#0000ff>/* If force is set do it anyway. */</font>
if (ia_valid & ATTR_FORCE)
goto fine;</pre>
<pre> /* Make sure a caller can chown. */<br> if ((ia_valid & ATTR_UID) &&<br> (current->fsuid != inode->i_uid ||<br> attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))<br> goto error;</pre>
<pre> /* Make sure caller can chgrp. */<br> if ((ia_valid & ATTR_GID) &&<br> (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid) &&<br> !capable(CAP_CHOWN))<br> goto error;</pre>
<pre> /* Make sure a caller can chmod. */<br> if (ia_valid & ATTR_MODE) {<br> if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))<br> goto error;<br> /* Also check the setgid bit! */<br> if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :<br> inode->i_gid) && !capable(CAP_FSETID))<br> attr->ia_mode &= ~S_ISGID;<br> }</pre>
<pre> /* Check for setting the inode time. */<br> if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {<br> if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))<br> goto error;<br> }<br>fine:<br> retval = 0;<br>error:<br> return retval;<br>}</pre>
<pre><font color=#0000ff>/*<br> * 根据attr的目的,设置inode的各种属性<br> * 要先调用inode_change_ok看看权限是否购,然后使用此函数<br> * 设置相应属性<br> */</font>
void inode_setattr(struct inode * inode, struct iattr * attr)
{
unsigned int ia_valid = attr->ia_valid;</pre>
<pre> if (ia_valid & ATTR_UID)<br> inode->i_uid = attr->ia_uid;<br> if (ia_valid & ATTR_GID)<br> inode->i_gid = attr->ia_gid;<br> if (ia_valid & ATTR_SIZE)<br> vmtruncate(inode, attr->ia_size);<br> if (ia_valid & ATTR_ATIME)<br> inode->i_atime = attr->ia_atime;<br> if (ia_valid & ATTR_MTIME)<br> inode->i_mtime = attr->ia_mtime;<br> if (ia_valid & ATTR_CTIME)<br> inode->i_ctime = attr->ia_ctime;<br> if (ia_valid & ATTR_MODE) {<br> inode->i_mode = attr->ia_mode;<br> if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))<br> inode->i_mode &= ~S_ISGID;<br> }<br> mark_inode_dirty(inode);<br>}</pre>
<pre><font color=#0000ff>/*<br> * 根据根该的属性,获得一个detnry notify 的mask, dn_mask<br> * inode->i_dnotify_mask 是进程所关心的事件,dn_mask是实际<br> * 发生的事件,相与即可得知是否需要通知进程<br> */</font>
static int setattr_mask(unsigned int ia_valid)
{
unsigned long dn_mask = 0;</pre>
<pre> if (ia_valid & ATTR_UID)<br> dn_mask |= DN_ATTRIB;<br> if (ia_valid & ATTR_GID)<br> dn_mask |= DN_ATTRIB;<br> if (ia_valid & ATTR_SIZE)<br> dn_mask |= DN_MODIFY;<br> /* both times implies a utime(s) call */<br> if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))<br> dn_mask |= DN_ATTRIB;<br> else if (ia_valid & ATTR_ATIME)<br> dn_mask |= DN_ACCESS;<br> else if (ia_valid & ATTR_MTIME)<br> dn_mask |= DN_MODIFY;<br> if (ia_valid & ATTR_MODE)<br> dn_mask |= DN_ATTRIB;<br> return dn_mask;<br>}</pre>
<pre><font color=#0000ff>/*<br> * 根据attr 设置inode的属性,并使用信号量通知相关进程<br> */</font>
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;</pre>
<pre> if (!inode)<br> BUG();</pre>
<pre> attr->ia_ctime = now;<br> if (!(ia_valid & ATTR_ATIME_SET))<br> attr->ia_atime = now;<br> if (!(ia_valid & ATTR_MTIME_SET))<br> attr->ia_mtime = now;</pre>
<pre> lock_kernel();<br> if (inode->i_op && inode->i_op->setattr) <br> <font color=#0000ff>/*ext2 未设置此op*/</font>
error = inode->i_op->setattr(dentry, attr);
else {
<font color=#0000ff>/*所以对ext2来讲执行此公共流程*/</font>
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;
}</pre>
<pre> </pre>
<pre> </pre>
<div align=center>
<pre><font color=#ff00ff size=4>dnotify.c<br> 2007.3.6</font></pre>
</div>
<div align=left>
<pre> 这个文件和此话题密切相关,顺便也解决了吧。想要监控一个文件的时候,需要首先打开这个文件,然后通过fcntl注册,就会通过信号<br>接受到这个文件相关属性的通知消息。<br> 为此,struct file中有一个f_owner,其中记录了打开这个文件的进程,此文件的等信息。fcntl的时候就需要设置好这个结构,notify<br>就是据此找到信号应该发给那个进程。<br> <br> 另外,需要在inode上挂一个dnotify_struct的链表,记录所有需要得到通知的file(pid记录于file-〉f_owner).此文件就是提供管理<br>这些信息的函数。<br> <br> +------+ <br> |inode | <br> +-/----+ <br> | <br> | +-------------+ +-------------+ <br> --------|dentry_notify|--|dentry_notify| <br> +------/------+ +-------------+ <br> | <br> | <br> | <br> +------\------------+ <br> | file->f_owner.pid | <br> +-------------------+ <br> 实在没有什么说的,见上图和这个两个简单的函数。 </pre>
<pre><font color=#0000ff>/*<br> * 将file->f_owner 和dn 的关系建立起来<br> */</font>
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;</pre>
<pre> if (!turning_off && !dir_notify_enable)<br> return -EINVAL;<br> inode = filp->f_dentry->d_inode;<br> if (!S_ISDIR(inode->i_mode))<br> return -ENOTDIR;<br> if (!turning_off) {<br> dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);<br> if (dn == NULL)<br> return -ENOMEM;<br> }<br> write_lock(&dn_lock);<br> prev = &inode->i_dnotify;<br> for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev)<br> if (odn->dn_filp == filp)<br> break;<br> if (odn != NULL) {<br> if (turning_off) {<br> *prev = odn->dn_next;<br> redo_inode_mask(inode);<br> dn = odn;<br> goto out_free;<br> }<br> odn->dn_fd = fd;<br> odn->dn_mask |= arg;<br> inode->i_dnotify_mask |=<font color=#0000ff> arg & ~DN_MULTISHOT</font>;<br> goto out_free;<br> }<br> if (turning_off)<br> goto out;<br> <font color=#0000ff>filp->f_owner.pid = current->pid;<br> filp->f_owner.uid = current->uid;<br> filp->f_owner.euid = current->euid;<br> dn->dn_magic = DNOTIFY_MAGIC;<br> dn->dn_mask = arg;<br> dn->dn_fd = fd;<br> dn->dn_filp = filp;<br> inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;</font>
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;
}</pre>
<pre><font color=#0000ff>/*<br> * 向所有监听此inode的进程发送信号<br> */</font>
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;</pre>
<pre> write_lock(&dn_lock);<br> prev = &inode->i_dnotify;<br> while ((dn = *prev) != NULL) {<br> if (dn->dn_magic != DNOTIFY_MAGIC) {<br> printk(KERN_ERR "__inode_dir_notify: bad magic "<br> "number in dnotify_struct!\n");<br> goto out;<br> }<br> if ((dn->dn_mask & event) == 0) {<br> prev = &dn->dn_next;<br> continue;<br> }<br> fown = &dn->dn_filp->f_owner;<br> if (fown->pid)<br> send_sigio(fown, dn->dn_fd, POLL_MSG);<br> <font color=#0000ff>if (dn->dn_mask & DN_MULTISHOT)<br> prev = &dn->dn_next;<br> else {<br> *prev = dn->dn_next;<br> changed = 1;<br> kmem_cache_free(dn_cache, dn);<br> }</font>
}
if (changed)
redo_inode_mask(inode);
out:
write_unlock(&dn_lock);
}</pre>
<pre> </pre>
</div>
<pre> </pre>
<pre> <br></pre>
</td>
</tr>
</tbody>
</table>
</div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -