fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / reiserfs / ioctl.c
1 /*
2  * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
3  */
4
5 #include <linux/capability.h>
6 #include <linux/fs.h>
7 #include <linux/mount.h>
8 #include <linux/reiserfs_fs.h>
9 #include <linux/time.h>
10 #include <asm/uaccess.h>
11 #include <linux/pagemap.h>
12 #include <linux/smp_lock.h>
13 #include <linux/compat.h>
14
15 static int reiserfs_unpack(struct inode *inode, struct file *filp);
16
17 /*
18 ** reiserfs_ioctl - handler for ioctl for inode
19 ** supported commands:
20 **  1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect
21 **                           and prevent packing file (argument arg has to be non-zero)
22 **  2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION
23 **  3) That's all for a while ...
24 */
25 int reiserfs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
26                    unsigned long arg)
27 {
28         unsigned int flags, oldflags;
29
30         switch (cmd) {
31         case REISERFS_IOC_UNPACK:
32                 if (S_ISREG(inode->i_mode)) {
33                         if (arg)
34                                 return reiserfs_unpack(inode, filp);
35                         else
36                                 return 0;
37                 } else
38                         return -ENOTTY;
39                 /* following two cases are taken from fs/ext2/ioctl.c by Remy
40                    Card (card@masi.ibp.fr) */
41         case REISERFS_IOC_GETFLAGS:
42                 if (!reiserfs_attrs(inode->i_sb))
43                         return -ENOTTY;
44
45                 flags = REISERFS_I(inode)->i_attrs;
46                 i_attrs_to_sd_attrs(inode, (__u16 *) & flags);
47                 flags &= REISERFS_FL_USER_VISIBLE;
48                 return put_user(flags, (int __user *)arg);
49         case REISERFS_IOC_SETFLAGS:{
50                         if (!reiserfs_attrs(inode->i_sb))
51                                 return -ENOTTY;
52
53                         if (IS_RDONLY(inode) ||
54                                 (filp && MNT_IS_RDONLY(filp->f_vfsmnt)))
55                                 return -EROFS;
56
57                         if ((current->fsuid != inode->i_uid)
58                             && !capable(CAP_FOWNER))
59                                 return -EPERM;
60
61                         if (get_user(flags, (int __user *)arg))
62                                 return -EFAULT;
63
64                         oldflags = REISERFS_I(inode) -> i_attrs;
65                         if (((oldflags & REISERFS_IMMUTABLE_FL) ||
66                                 ((flags ^ oldflags) &
67                                 (REISERFS_IMMUTABLE_FL | REISERFS_IUNLINK_FL |
68                                  REISERFS_APPEND_FL))) &&
69                                 !capable(CAP_LINUX_IMMUTABLE))
70                                 return -EPERM;
71
72                         if ((flags & REISERFS_NOTAIL_FL) &&
73                             S_ISREG(inode->i_mode)) {
74                                 int result;
75
76                                 result = reiserfs_unpack(inode, filp);
77                                 if (result)
78                                         return result;
79                         }
80
81                         flags = flags & REISERFS_FL_USER_MODIFIABLE;
82                         flags |= oldflags & ~REISERFS_FL_USER_MODIFIABLE;
83                         sd_attrs_to_i_attrs(flags, inode);
84                         REISERFS_I(inode)->i_attrs = flags;
85                         inode->i_ctime = CURRENT_TIME_SEC;
86                         mark_inode_dirty(inode);
87                         return 0;
88                 }
89         case REISERFS_IOC_GETVERSION:
90                 return put_user(inode->i_generation, (int __user *)arg);
91         case REISERFS_IOC_SETVERSION:
92                 if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
93                         return -EPERM;
94                 if (IS_RDONLY(inode) ||
95                         (filp && MNT_IS_RDONLY(filp->f_vfsmnt)))
96                         return -EROFS;
97                 if (get_user(inode->i_generation, (int __user *)arg))
98                         return -EFAULT;
99                 inode->i_ctime = CURRENT_TIME_SEC;
100                 mark_inode_dirty(inode);
101                 return 0;
102         default:
103                 return -ENOTTY;
104         }
105 }
106
107 #ifdef CONFIG_COMPAT
108 long reiserfs_compat_ioctl(struct file *file, unsigned int cmd,
109                                 unsigned long arg)
110 {
111         struct inode *inode = file->f_path.dentry->d_inode;
112         int ret;
113
114         /* These are just misnamed, they actually get/put from/to user an int */
115         switch (cmd) {
116         case REISERFS_IOC32_UNPACK:
117                 cmd = REISERFS_IOC_UNPACK;
118                 break;
119         case REISERFS_IOC32_GETFLAGS:
120                 cmd = REISERFS_IOC_GETFLAGS;
121                 break;
122         case REISERFS_IOC32_SETFLAGS:
123                 cmd = REISERFS_IOC_SETFLAGS;
124                 break;
125         case REISERFS_IOC32_GETVERSION:
126                 cmd = REISERFS_IOC_GETVERSION;
127                 break;
128         case REISERFS_IOC32_SETVERSION:
129                 cmd = REISERFS_IOC_SETVERSION;
130                 break;
131         default:
132                 return -ENOIOCTLCMD;
133         }
134         lock_kernel();
135         ret = reiserfs_ioctl(inode, file, cmd, (unsigned long) compat_ptr(arg));
136         unlock_kernel();
137         return ret;
138 }
139 #endif
140
141 /*
142 ** reiserfs_unpack
143 ** Function try to convert tail from direct item into indirect.
144 ** It set up nopack attribute in the REISERFS_I(inode)->nopack
145 */
146 static int reiserfs_unpack(struct inode *inode, struct file *filp)
147 {
148         int retval = 0;
149         int index;
150         struct page *page;
151         struct address_space *mapping;
152         unsigned long write_from;
153         unsigned long blocksize = inode->i_sb->s_blocksize;
154
155         if (inode->i_size == 0) {
156                 REISERFS_I(inode)->i_flags |= i_nopack_mask;
157                 return 0;
158         }
159         /* ioctl already done */
160         if (REISERFS_I(inode)->i_flags & i_nopack_mask) {
161                 return 0;
162         }
163
164         /* we need to make sure nobody is changing the file size beneath
165          ** us
166          */
167         mutex_lock(&inode->i_mutex);
168         reiserfs_write_lock(inode->i_sb);
169
170         write_from = inode->i_size & (blocksize - 1);
171         /* if we are on a block boundary, we are already unpacked.  */
172         if (write_from == 0) {
173                 REISERFS_I(inode)->i_flags |= i_nopack_mask;
174                 goto out;
175         }
176
177         /* we unpack by finding the page with the tail, and calling
178          ** reiserfs_prepare_write on that page.  This will force a 
179          ** reiserfs_get_block to unpack the tail for us.
180          */
181         index = inode->i_size >> PAGE_CACHE_SHIFT;
182         mapping = inode->i_mapping;
183         page = grab_cache_page(mapping, index);
184         retval = -ENOMEM;
185         if (!page) {
186                 goto out;
187         }
188         retval =
189             mapping->a_ops->prepare_write(NULL, page, write_from, write_from);
190         if (retval)
191                 goto out_unlock;
192
193         /* conversion can change page contents, must flush */
194         flush_dcache_page(page);
195         retval =
196             mapping->a_ops->commit_write(NULL, page, write_from, write_from);
197         REISERFS_I(inode)->i_flags |= i_nopack_mask;
198
199       out_unlock:
200         unlock_page(page);
201         page_cache_release(page);
202
203       out:
204         mutex_unlock(&inode->i_mutex);
205         reiserfs_write_unlock(inode->i_sb);
206         return retval;
207 }