2005-04-17 02:20:36 +04:00
/*
* linux / fs / attr . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* changes by Thomas Schoebel - Theuer
*/
# include <linux/module.h>
# include <linux/time.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/smp_lock.h>
[PATCH] inotify
inotify is intended to correct the deficiencies of dnotify, particularly
its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory
that you intend to watch. This quickly results in too many
open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to
directories. Sure, a change to a file in a directory affects
the directory, but you are then forced to keep a cache of
stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change
notification:
* inotify's interface is a system call that returns a fd, not SIGIO.
You get a single fd, which is select()-able.
* inotify has an event that says "the filesystem that the item
you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure),
Gamin (a FAM replacement), and other projects.
See Documentation/filesystems/inotify.txt.
Signed-off-by: Robert Love <rml@novell.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-07-13 01:06:03 +04:00
# include <linux/fsnotify.h>
2005-04-17 02:20:36 +04:00
# include <linux/fcntl.h>
# include <linux/quotaops.h>
# include <linux/security.h>
# include <linux/time.h>
/* Taken over from the old code... */
/* 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 ) & &
( current - > fsuid ! = inode - > i_uid | |
( ! 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 ;
}
EXPORT_SYMBOL ( inode_change_ok ) ;
int inode_setattr ( struct inode * inode , struct iattr * attr )
{
unsigned int ia_valid = attr - > ia_valid ;
int error = 0 ;
if ( ia_valid & ATTR_SIZE ) {
if ( attr - > ia_size ! = i_size_read ( inode ) ) {
error = vmtruncate ( inode , attr - > ia_size ) ;
if ( error | | ( ia_valid = = ATTR_SIZE ) )
goto out ;
} else {
/*
* We skipped the truncate but must still update
* timestamps
*/
ia_valid | = ATTR_MTIME | ATTR_CTIME ;
}
}
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 ;
}
mark_inode_dirty ( inode ) ;
out :
return error ;
}
EXPORT_SYMBOL ( inode_setattr ) ;
int notify_change ( struct dentry * dentry , struct iattr * attr )
{
struct inode * inode = dentry - > d_inode ;
mode_t mode ;
int error ;
struct timespec now ;
unsigned int ia_valid = attr - > ia_valid ;
mode = inode - > i_mode ;
now = current_fs_time ( inode - > i_sb ) ;
attr - > ia_ctime = now ;
if ( ! ( ia_valid & ATTR_ATIME_SET ) )
attr - > ia_atime = now ;
if ( ! ( ia_valid & ATTR_MTIME_SET ) )
attr - > ia_mtime = now ;
if ( ia_valid & ATTR_KILL_SUID ) {
attr - > ia_valid & = ~ ATTR_KILL_SUID ;
if ( mode & S_ISUID ) {
if ( ! ( ia_valid & ATTR_MODE ) ) {
ia_valid = attr - > ia_valid | = ATTR_MODE ;
attr - > ia_mode = inode - > i_mode ;
}
attr - > ia_mode & = ~ S_ISUID ;
}
}
if ( ia_valid & ATTR_KILL_SGID ) {
attr - > ia_valid & = ~ ATTR_KILL_SGID ;
if ( ( mode & ( S_ISGID | S_IXGRP ) ) = = ( S_ISGID | S_IXGRP ) ) {
if ( ! ( ia_valid & ATTR_MODE ) ) {
ia_valid = attr - > ia_valid | = ATTR_MODE ;
attr - > ia_mode = inode - > i_mode ;
}
attr - > ia_mode & = ~ S_ISGID ;
}
}
if ( ! attr - > ia_valid )
return 0 ;
if ( ia_valid & ATTR_SIZE )
down_write ( & dentry - > d_inode - > i_alloc_sem ) ;
if ( inode - > i_op & & inode - > i_op - > setattr ) {
error = security_inode_setattr ( dentry , attr ) ;
if ( ! error )
error = inode - > i_op - > setattr ( dentry , attr ) ;
} else {
error = inode_change_ok ( inode , attr ) ;
if ( ! error )
error = security_inode_setattr ( dentry , attr ) ;
if ( ! error ) {
if ( ( ia_valid & ATTR_UID & & attr - > ia_uid ! = inode - > i_uid ) | |
( ia_valid & ATTR_GID & & attr - > ia_gid ! = inode - > i_gid ) )
error = DQUOT_TRANSFER ( inode , attr ) ? - EDQUOT : 0 ;
if ( ! error )
error = inode_setattr ( inode , attr ) ;
}
}
if ( ia_valid & ATTR_SIZE )
up_write ( & dentry - > d_inode - > i_alloc_sem ) ;
[PATCH] inotify
inotify is intended to correct the deficiencies of dnotify, particularly
its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory
that you intend to watch. This quickly results in too many
open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to
directories. Sure, a change to a file in a directory affects
the directory, but you are then forced to keep a cache of
stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change
notification:
* inotify's interface is a system call that returns a fd, not SIGIO.
You get a single fd, which is select()-able.
* inotify has an event that says "the filesystem that the item
you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure),
Gamin (a FAM replacement), and other projects.
See Documentation/filesystems/inotify.txt.
Signed-off-by: Robert Love <rml@novell.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-07-13 01:06:03 +04:00
if ( ! error )
fsnotify_change ( dentry , ia_valid ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
EXPORT_SYMBOL ( notify_change ) ;