]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/patches/suse-2.6.27.25/patches.apparmor/unambiguous-__d_path.diff
Revert "Disable build of xen kernel."
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.apparmor / unambiguous-__d_path.diff
1 From: Andreas Gruenbacher <agruen@suse.de>
2 Subject: Fix __d_path() for lazy unmounts and make it unambiguous
3
4 First, when __d_path() hits a lazily unmounted mount point, it tries to prepend
5 the name of the lazily unmounted dentry to the path name. It gets this wrong,
6 and also overwrites the slash that separates the name from the following
7 pathname component. This patch fixes that; if a process was in directory
8 /foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the
9 missing slash), while the new result with this patch is ``foo/bar''.
10
11 Second, it isn't always possible to tell from the __d_path() result whether the
12 specified root and rootmnt (i.e., the chroot) was reached. We need an
13 unambiguous result for AppArmor at least though, so we make sure that paths
14 will only start with a slash if the path leads all the way up to the root.
15
16 We also add a @fail_deleted argument, which allows to get rid of some of the
17 mess in sys_getcwd().
18
19 This patch leaves getcwd() and d_path() as they were before for everything
20 except for bind-mounted directories; for them, it reports ``/foo/bar'' instead
21 of ``foobar'' in the example described above.
22
23 Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
24 Signed-off-by: John Johansen <jjohansen@suse.de>
25 Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
26
27 [ Moved dcache_lock outside vfsmount_lock to fix lock order (bnc#490902) ]
28 Signed-off-by: Nick Piggin <npiggin@suse.de>
29
30 ---
31 fs/dcache.c | 126 +++++++++++++++++++++++++++----------------------
32 fs/seq_file.c | 4 -
33 include/linux/dcache.h | 5 +
34 3 files changed, 75 insertions(+), 60 deletions(-)
35
36 Index: linux-2.6.27/fs/dcache.c
37 ===================================================================
38 --- linux-2.6.27.orig/fs/dcache.c
39 +++ linux-2.6.27/fs/dcache.c
40 @@ -1898,44 +1898,46 @@ static int prepend_name(char **buffer, i
41 * @root: root vfsmnt/dentry (may be modified by this function)
42 * @buffer: buffer to return value in
43 * @buflen: buffer length
44 + * @flags: flags controling behavior of d_path
45 *
46 - * Convert a dentry into an ASCII path name. If the entry has been deleted
47 - * the string " (deleted)" is appended. Note that this is ambiguous.
48 - *
49 - * Returns the buffer or an error code if the path was too long.
50 - *
51 - * "buflen" should be positive. Caller holds the dcache_lock.
52 + * Convert a dentry into an ASCII path name. If the entry has been deleted,
53 + * then if @flags has D_PATH_FAIL_DELETED set, ERR_PTR(-ENOENT) is returned.
54 + * Otherwise, the string " (deleted)" is appended. Note that this is ambiguous.
55 *
56 * If path is not reachable from the supplied root, then the value of
57 - * root is changed (without modifying refcounts).
58 + * root is changed (without modifying refcounts). The path returned in this
59 + * case will be relative (i.e., it will not start with a slash).
60 + *
61 + * Returns the buffer or an error code if the path was too long.
62 */
63 char *__d_path(const struct path *path, struct path *root,
64 - char *buffer, int buflen)
65 + char *buffer, int buflen, int flags)
66 {
67 struct dentry *dentry = path->dentry;
68 struct vfsmount *vfsmnt = path->mnt;
69 - char *end = buffer + buflen;
70 - char *retval;
71 + const unsigned char *name;
72 + int namelen;
73 +
74 + buffer += buflen;
75 + prepend(&buffer, &buflen, "\0", 1);
76
77 + spin_lock(&dcache_lock);
78 spin_lock(&vfsmount_lock);
79 - prepend(&end, &buflen, "\0", 1);
80 - if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
81 - (prepend(&end, &buflen, " (deleted)", 10) != 0))
82 + if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
83 + if (flags & D_PATH_FAIL_DELETED) {
84 + buffer = ERR_PTR(-ENOENT);
85 + goto out;
86 + }
87 + if (prepend(&buffer, &buflen, " (deleted)", 10) != 0)
88 goto Elong;
89 -
90 + }
91 if (buflen < 1)
92 goto Elong;
93 - /* Get '/' right */
94 - retval = end-1;
95 - *retval = '/';
96
97 - for (;;) {
98 + while (dentry != root->dentry || vfsmnt != root->mnt) {
99 struct dentry * parent;
100
101 - if (dentry == root->dentry && vfsmnt == root->mnt)
102 - break;
103 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
104 - /* Global root? */
105 if (vfsmnt->mnt_parent == vfsmnt) {
106 goto global_root;
107 }
108 @@ -1945,27 +1947,51 @@ char *__d_path(const struct path *path,
109 }
110 parent = dentry->d_parent;
111 prefetch(parent);
112 - if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
113 - (prepend(&end, &buflen, "/", 1) != 0))
114 + if ((prepend_name(&buffer, &buflen, &dentry->d_name) != 0) ||
115 + (prepend(&buffer, &buflen, "/", 1) != 0))
116 goto Elong;
117 - retval = end;
118 dentry = parent;
119 }
120 + /* Get '/' right. */
121 + if (*buffer != '/' && prepend(&buffer, &buflen, "/", 1))
122 + goto Elong;
123
124 out:
125 spin_unlock(&vfsmount_lock);
126 - return retval;
127 + spin_unlock(&dcache_lock);
128 + return buffer;
129
130 global_root:
131 - retval += 1; /* hit the slash */
132 - if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
133 + /*
134 + * We went past the (vfsmount, dentry) we were looking for and have
135 + * either hit a root dentry, a lazily unmounted dentry, an
136 + * unconnected dentry, or the file is on a pseudo filesystem.
137 + */
138 + namelen = dentry->d_name.len;
139 + name = dentry->d_name.name;
140 +
141 + /*
142 + * If this is a root dentry, then overwrite the slash. This
143 + * will also DTRT with pseudo filesystems which have root
144 + * dentries named "foo:".
145 + */
146 + if (IS_ROOT(dentry) && *buffer == '/') {
147 + buffer++;
148 + buflen++;
149 + }
150 + if ((flags & D_PATH_DISCONNECT) && *name == '/') {
151 + /* Make sure we won't return a pathname starting with '/' */
152 + name++;
153 + namelen--;
154 + }
155 + if (prepend(&buffer, &buflen, name, namelen))
156 goto Elong;
157 root->mnt = vfsmnt;
158 root->dentry = dentry;
159 goto out;
160
161 Elong:
162 - retval = ERR_PTR(-ENAMETOOLONG);
163 + buffer = ERR_PTR(-ENAMETOOLONG);
164 goto out;
165 }
166
167 @@ -2002,10 +2028,8 @@ char *d_path(const struct path *path, ch
168 root = current->fs->root;
169 path_get(&root);
170 read_unlock(&current->fs->lock);
171 - spin_lock(&dcache_lock);
172 tmp = root;
173 - res = __d_path(path, &tmp, buf, buflen);
174 - spin_unlock(&dcache_lock);
175 + res = __d_path(path, &tmp, buf, buflen, 0);
176 path_put(&root);
177 return res;
178 }
179 @@ -2088,9 +2112,9 @@ Elong:
180 */
181 SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
182 {
183 - int error;
184 - struct path pwd, root;
185 - char *page = (char *) __get_free_page(GFP_USER);
186 + int error, len;
187 + struct path pwd, root, tmp;
188 + char *page = (char *) __get_free_page(GFP_USER), *cwd;
189
190 if (!page)
191 return -ENOMEM;
192 @@ -2102,30 +2126,20 @@ SYSCALL_DEFINE2(getcwd, char __user *, b
193 path_get(&root);
194 read_unlock(&current->fs->lock);
195
196 - error = -ENOENT;
197 - /* Has the current directory has been unlinked? */
198 - spin_lock(&dcache_lock);
199 - if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) {
200 - unsigned long len;
201 - struct path tmp = root;
202 - char * cwd;
203 -
204 - cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
205 - spin_unlock(&dcache_lock);
206 -
207 + tmp = root;
208 + cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE, D_PATH_FAIL_DELETED);
209 + if (IS_ERR(cwd)) {
210 error = PTR_ERR(cwd);
211 - if (IS_ERR(cwd))
212 - goto out;
213 + goto out;
214 + }
215
216 - error = -ERANGE;
217 - len = PAGE_SIZE + page - cwd;
218 - if (len <= size) {
219 - error = len;
220 - if (copy_to_user(buf, cwd, len))
221 - error = -EFAULT;
222 - }
223 - } else
224 - spin_unlock(&dcache_lock);
225 + error = -ERANGE;
226 + len = PAGE_SIZE + page - cwd;
227 + if (len <= size) {
228 + error = len;
229 + if (copy_to_user(buf, cwd, len))
230 + error = -EFAULT;
231 + }
232
233 out:
234 path_put(&pwd);
235 Index: linux-2.6.27/fs/seq_file.c
236 ===================================================================
237 --- linux-2.6.27.orig/fs/seq_file.c
238 +++ linux-2.6.27/fs/seq_file.c
239 @@ -441,9 +441,7 @@ int seq_path_root(struct seq_file *m, st
240 char *s = m->buf + m->count;
241 char *p;
242
243 - spin_lock(&dcache_lock);
244 - p = __d_path(path, root, s, m->size - m->count);
245 - spin_unlock(&dcache_lock);
246 + p = __d_path(path, root, s, m->size - m->count, 0);
247 err = PTR_ERR(p);
248 if (!IS_ERR(p)) {
249 s = mangle_path(s, p, esc);
250 Index: linux-2.6.27/include/linux/dcache.h
251 ===================================================================
252 --- linux-2.6.27.orig/include/linux/dcache.h
253 +++ linux-2.6.27/include/linux/dcache.h
254 @@ -299,9 +299,12 @@ extern int d_validate(struct dentry *, s
255 /*
256 * helper function for dentry_operations.d_dname() members
257 */
258 +#define D_PATH_FAIL_DELETED 1
259 +#define D_PATH_DISCONNECT 2
260 extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
261
262 -extern char *__d_path(const struct path *path, struct path *root, char *, int);
263 +extern char *__d_path(const struct path *path, struct path *root, char *, int,
264 + int);
265 extern char *d_path(const struct path *, char *, int);
266 extern char *dentry_path(struct dentry *, char *, int);
267