diff options
Diffstat (limited to 'fs/reiserfs/ioctl.c')
-rw-r--r-- | fs/reiserfs/ioctl.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c new file mode 100644 index 000000000000..94dc42475a04 --- /dev/null +++ b/fs/reiserfs/ioctl.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README | ||
3 | */ | ||
4 | |||
5 | #include <linux/fs.h> | ||
6 | #include <linux/reiserfs_fs.h> | ||
7 | #include <linux/time.h> | ||
8 | #include <asm/uaccess.h> | ||
9 | #include <linux/pagemap.h> | ||
10 | #include <linux/smp_lock.h> | ||
11 | |||
12 | static int reiserfs_unpack (struct inode * inode, struct file * filp); | ||
13 | |||
14 | /* | ||
15 | ** reiserfs_ioctl - handler for ioctl for inode | ||
16 | ** supported commands: | ||
17 | ** 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect | ||
18 | ** and prevent packing file (argument arg has to be non-zero) | ||
19 | ** 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION | ||
20 | ** 3) That's all for a while ... | ||
21 | */ | ||
22 | int reiserfs_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, | ||
23 | unsigned long arg) | ||
24 | { | ||
25 | unsigned int flags; | ||
26 | |||
27 | switch (cmd) { | ||
28 | case REISERFS_IOC_UNPACK: | ||
29 | if( S_ISREG( inode -> i_mode ) ) { | ||
30 | if (arg) | ||
31 | return reiserfs_unpack (inode, filp); | ||
32 | else | ||
33 | return 0; | ||
34 | } else | ||
35 | return -ENOTTY; | ||
36 | /* following two cases are taken from fs/ext2/ioctl.c by Remy | ||
37 | Card (card@masi.ibp.fr) */ | ||
38 | case REISERFS_IOC_GETFLAGS: | ||
39 | flags = REISERFS_I(inode) -> i_attrs; | ||
40 | i_attrs_to_sd_attrs( inode, ( __u16 * ) &flags ); | ||
41 | return put_user(flags, (int __user *) arg); | ||
42 | case REISERFS_IOC_SETFLAGS: { | ||
43 | if (IS_RDONLY(inode)) | ||
44 | return -EROFS; | ||
45 | |||
46 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
47 | return -EPERM; | ||
48 | |||
49 | if (get_user(flags, (int __user *) arg)) | ||
50 | return -EFAULT; | ||
51 | |||
52 | if ( ( ( flags ^ REISERFS_I(inode) -> i_attrs) & ( REISERFS_IMMUTABLE_FL | REISERFS_APPEND_FL)) && | ||
53 | !capable( CAP_LINUX_IMMUTABLE ) ) | ||
54 | return -EPERM; | ||
55 | |||
56 | if( ( flags & REISERFS_NOTAIL_FL ) && | ||
57 | S_ISREG( inode -> i_mode ) ) { | ||
58 | int result; | ||
59 | |||
60 | result = reiserfs_unpack( inode, filp ); | ||
61 | if( result ) | ||
62 | return result; | ||
63 | } | ||
64 | sd_attrs_to_i_attrs( flags, inode ); | ||
65 | REISERFS_I(inode) -> i_attrs = flags; | ||
66 | inode->i_ctime = CURRENT_TIME_SEC; | ||
67 | mark_inode_dirty(inode); | ||
68 | return 0; | ||
69 | } | ||
70 | case REISERFS_IOC_GETVERSION: | ||
71 | return put_user(inode->i_generation, (int __user *) arg); | ||
72 | case REISERFS_IOC_SETVERSION: | ||
73 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
74 | return -EPERM; | ||
75 | if (IS_RDONLY(inode)) | ||
76 | return -EROFS; | ||
77 | if (get_user(inode->i_generation, (int __user *) arg)) | ||
78 | return -EFAULT; | ||
79 | inode->i_ctime = CURRENT_TIME_SEC; | ||
80 | mark_inode_dirty(inode); | ||
81 | return 0; | ||
82 | default: | ||
83 | return -ENOTTY; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | ** reiserfs_unpack | ||
89 | ** Function try to convert tail from direct item into indirect. | ||
90 | ** It set up nopack attribute in the REISERFS_I(inode)->nopack | ||
91 | */ | ||
92 | static int reiserfs_unpack (struct inode * inode, struct file * filp) | ||
93 | { | ||
94 | int retval = 0; | ||
95 | int index ; | ||
96 | struct page *page ; | ||
97 | struct address_space *mapping ; | ||
98 | unsigned long write_from ; | ||
99 | unsigned long blocksize = inode->i_sb->s_blocksize ; | ||
100 | |||
101 | if (inode->i_size == 0) { | ||
102 | REISERFS_I(inode)->i_flags |= i_nopack_mask; | ||
103 | return 0 ; | ||
104 | } | ||
105 | /* ioctl already done */ | ||
106 | if (REISERFS_I(inode)->i_flags & i_nopack_mask) { | ||
107 | return 0 ; | ||
108 | } | ||
109 | reiserfs_write_lock(inode->i_sb); | ||
110 | |||
111 | /* we need to make sure nobody is changing the file size beneath | ||
112 | ** us | ||
113 | */ | ||
114 | down(&inode->i_sem) ; | ||
115 | |||
116 | write_from = inode->i_size & (blocksize - 1) ; | ||
117 | /* if we are on a block boundary, we are already unpacked. */ | ||
118 | if ( write_from == 0) { | ||
119 | REISERFS_I(inode)->i_flags |= i_nopack_mask; | ||
120 | goto out ; | ||
121 | } | ||
122 | |||
123 | /* we unpack by finding the page with the tail, and calling | ||
124 | ** reiserfs_prepare_write on that page. This will force a | ||
125 | ** reiserfs_get_block to unpack the tail for us. | ||
126 | */ | ||
127 | index = inode->i_size >> PAGE_CACHE_SHIFT ; | ||
128 | mapping = inode->i_mapping ; | ||
129 | page = grab_cache_page(mapping, index) ; | ||
130 | retval = -ENOMEM; | ||
131 | if (!page) { | ||
132 | goto out ; | ||
133 | } | ||
134 | retval = mapping->a_ops->prepare_write(NULL, page, write_from, write_from) ; | ||
135 | if (retval) | ||
136 | goto out_unlock ; | ||
137 | |||
138 | /* conversion can change page contents, must flush */ | ||
139 | flush_dcache_page(page) ; | ||
140 | retval = mapping->a_ops->commit_write(NULL, page, write_from, write_from) ; | ||
141 | REISERFS_I(inode)->i_flags |= i_nopack_mask; | ||
142 | |||
143 | out_unlock: | ||
144 | unlock_page(page) ; | ||
145 | page_cache_release(page) ; | ||
146 | |||
147 | out: | ||
148 | up(&inode->i_sem) ; | ||
149 | reiserfs_write_unlock(inode->i_sb); | ||
150 | return retval; | ||
151 | } | ||