Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / fs / aufs / ioctl.c
1 /*
2  * Copyright (C) 2005-2013 Junjiro R. Okajima
3  *
4  * This program, aufs is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 /*
20  * ioctl
21  * plink-management and readdir in userspace.
22  * assist the pathconf(3) wrapper library.
23  */
24
25 #include "aufs.h"
26
27 static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
28 {
29         int err, fd;
30         aufs_bindex_t wbi, bindex, bend;
31         struct file *h_file;
32         struct super_block *sb;
33         struct dentry *root;
34         struct au_branch *br;
35         struct aufs_wbr_fd wbrfd = {
36                 .oflags = au_dir_roflags,
37                 .brid   = -1
38         };
39         const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
40                 | O_NOATIME | O_CLOEXEC;
41
42         AuDebugOn(wbrfd.oflags & ~valid);
43
44         if (arg) {
45                 err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
46                 if (unlikely(err)) {
47                         err = -EFAULT;
48                         goto out;
49                 }
50
51                 err = -EINVAL;
52                 AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
53                 wbrfd.oflags |= au_dir_roflags;
54                 AuDbg("0%o\n", wbrfd.oflags);
55                 if (unlikely(wbrfd.oflags & ~valid))
56                         goto out;
57         }
58
59         fd = get_unused_fd();
60         err = fd;
61         if (unlikely(fd < 0))
62                 goto out;
63
64         h_file = ERR_PTR(-EINVAL);
65         wbi = 0;
66         br = NULL;
67         sb = path->dentry->d_sb;
68         root = sb->s_root;
69         aufs_read_lock(root, AuLock_IR);
70         bend = au_sbend(sb);
71         if (wbrfd.brid >= 0) {
72                 wbi = au_br_index(sb, wbrfd.brid);
73                 if (unlikely(wbi < 0 || wbi > bend))
74                         goto out_unlock;
75         }
76
77         h_file = ERR_PTR(-ENOENT);
78         br = au_sbr(sb, wbi);
79         if (!au_br_writable(br->br_perm)) {
80                 if (arg)
81                         goto out_unlock;
82
83                 bindex = wbi + 1;
84                 wbi = -1;
85                 for (; bindex <= bend; bindex++) {
86                         br = au_sbr(sb, bindex);
87                         if (au_br_writable(br->br_perm)) {
88                                 wbi = bindex;
89                                 br = au_sbr(sb, wbi);
90                                 break;
91                         }
92                 }
93         }
94         AuDbg("wbi %d\n", wbi);
95         if (wbi >= 0)
96                 h_file = au_h_open(root, wbi, wbrfd.oflags, NULL);
97
98 out_unlock:
99         aufs_read_unlock(root, AuLock_IR);
100         err = PTR_ERR(h_file);
101         if (IS_ERR(h_file))
102                 goto out_fd;
103
104         atomic_dec(&br->br_count); /* cf. au_h_open() */
105         fd_install(fd, h_file);
106         err = fd;
107         goto out; /* success */
108
109 out_fd:
110         put_unused_fd(fd);
111 out:
112         AuTraceErr(err);
113         return err;
114 }
115
116 /* ---------------------------------------------------------------------- */
117
118 long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
119 {
120         long err;
121
122         switch (cmd) {
123         case AUFS_CTL_RDU:
124         case AUFS_CTL_RDU_INO:
125                 err = au_rdu_ioctl(file, cmd, arg);
126                 break;
127
128         case AUFS_CTL_WBR_FD:
129                 err = au_wbr_fd(&file->f_path, (void __user *)arg);
130                 break;
131
132         case AUFS_CTL_IBUSY:
133                 err = au_ibusy_ioctl(file, arg);
134                 break;
135
136         default:
137                 /* do not call the lower */
138                 AuDbg("0x%x\n", cmd);
139                 err = -ENOTTY;
140         }
141
142         AuTraceErr(err);
143         return err;
144 }
145
146 long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg)
147 {
148         long err;
149
150         switch (cmd) {
151         case AUFS_CTL_WBR_FD:
152                 err = au_wbr_fd(&file->f_path, (void __user *)arg);
153                 break;
154
155         default:
156                 /* do not call the lower */
157                 AuDbg("0x%x\n", cmd);
158                 err = -ENOTTY;
159         }
160
161         AuTraceErr(err);
162         return err;
163 }
164
165 #ifdef CONFIG_COMPAT
166 long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
167                            unsigned long arg)
168 {
169         long err;
170
171         switch (cmd) {
172         case AUFS_CTL_RDU:
173         case AUFS_CTL_RDU_INO:
174                 err = au_rdu_compat_ioctl(file, cmd, arg);
175                 break;
176
177         case AUFS_CTL_IBUSY:
178                 err = au_ibusy_compat_ioctl(file, arg);
179                 break;
180
181         default:
182                 err = aufs_ioctl_dir(file, cmd, arg);
183         }
184
185         AuTraceErr(err);
186         return err;
187 }
188
189 #if 0 /* unused yet */
190 long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
191                               unsigned long arg)
192 {
193         return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg));
194 }
195 #endif
196 #endif