1010#include <linux/fs.h>
1111#include <linux/namei.h>
1212#include <linux/xattr.h>
13+ #include <linux/ratelimit.h>
1314#include "overlayfs.h"
1415#include "ovl_entry.h"
1516
@@ -19,8 +20,66 @@ struct ovl_lookup_data {
1920 bool opaque ;
2021 bool stop ;
2122 bool last ;
23+ char * redirect ;
2224};
2325
26+ static int ovl_check_redirect (struct dentry * dentry , struct ovl_lookup_data * d ,
27+ size_t prelen , const char * post )
28+ {
29+ int res ;
30+ char * s , * next , * buf = NULL ;
31+
32+ res = vfs_getxattr (dentry , OVL_XATTR_REDIRECT , NULL , 0 );
33+ if (res < 0 ) {
34+ if (res == - ENODATA || res == - EOPNOTSUPP )
35+ return 0 ;
36+ goto fail ;
37+ }
38+ buf = kzalloc (prelen + res + strlen (post ) + 1 , GFP_TEMPORARY );
39+ if (!buf )
40+ return - ENOMEM ;
41+
42+ if (res == 0 )
43+ goto invalid ;
44+
45+ res = vfs_getxattr (dentry , OVL_XATTR_REDIRECT , buf , res );
46+ if (res < 0 )
47+ goto fail ;
48+ if (res == 0 )
49+ goto invalid ;
50+ if (buf [0 ] == '/' ) {
51+ for (s = buf ; * s ++ == '/' ; s = next ) {
52+ next = strchrnul (s , '/' );
53+ if (s == next )
54+ goto invalid ;
55+ }
56+ } else {
57+ if (strchr (buf , '/' ) != NULL )
58+ goto invalid ;
59+
60+ memmove (buf + prelen , buf , res );
61+ memcpy (buf , d -> name .name , prelen );
62+ }
63+
64+ strcat (buf , post );
65+ kfree (d -> redirect );
66+ d -> redirect = buf ;
67+ d -> name .name = d -> redirect ;
68+ d -> name .len = strlen (d -> redirect );
69+
70+ return 0 ;
71+
72+ err_free :
73+ kfree (buf );
74+ return 0 ;
75+ fail :
76+ pr_warn_ratelimited ("overlayfs: failed to get redirect (%i)\n" , res );
77+ goto err_free ;
78+ invalid :
79+ pr_warn_ratelimited ("overlayfs: invalid redirect (%s)\n" , buf );
80+ goto err_free ;
81+ }
82+
2483static bool ovl_is_opaquedir (struct dentry * dentry )
2584{
2685 int res ;
@@ -38,6 +97,7 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
3897
3998static int ovl_lookup_single (struct dentry * base , struct ovl_lookup_data * d ,
4099 const char * name , unsigned int namelen ,
100+ size_t prelen , const char * post ,
41101 struct dentry * * ret )
42102{
43103 struct dentry * this ;
@@ -74,6 +134,9 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
74134 d -> stop = d -> opaque = true;
75135 goto out ;
76136 }
137+ err = ovl_check_redirect (this , d , prelen , post );
138+ if (err )
139+ goto out_err ;
77140out :
78141 * ret = this ;
79142 return 0 ;
@@ -91,7 +154,32 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
91154static int ovl_lookup_layer (struct dentry * base , struct ovl_lookup_data * d ,
92155 struct dentry * * ret )
93156{
94- return ovl_lookup_single (base , d , d -> name .name , d -> name .len , ret );
157+ const char * s = d -> name .name ;
158+ struct dentry * dentry = NULL ;
159+ int err ;
160+
161+ if (* s != '/' )
162+ return ovl_lookup_single (base , d , d -> name .name , d -> name .len ,
163+ 0 , "" , ret );
164+
165+ while (* s ++ == '/' && !IS_ERR_OR_NULL (base ) && d_can_lookup (base )) {
166+ const char * next = strchrnul (s , '/' );
167+ size_t slen = strlen (s );
168+
169+ if (WARN_ON (slen > d -> name .len ) ||
170+ WARN_ON (strcmp (d -> name .name + d -> name .len - slen , s )))
171+ return - EIO ;
172+
173+ err = ovl_lookup_single (base , d , s , next - s ,
174+ d -> name .len - slen , next , & base );
175+ dput (dentry );
176+ if (err )
177+ return err ;
178+ dentry = base ;
179+ s = next ;
180+ }
181+ * ret = dentry ;
182+ return 0 ;
95183}
96184
97185/*
@@ -127,6 +215,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
127215 unsigned int ctr = 0 ;
128216 struct inode * inode = NULL ;
129217 bool upperopaque = false;
218+ char * upperredirect = NULL ;
130219 struct dentry * this ;
131220 unsigned int i ;
132221 int err ;
@@ -136,6 +225,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
136225 .opaque = false,
137226 .stop = false,
138227 .last = !poe -> numlower ,
228+ .redirect = NULL ,
139229 };
140230
141231 if (dentry -> d_name .len > ofs -> namelen )
@@ -153,12 +243,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
153243 err = - EREMOTE ;
154244 goto out ;
155245 }
246+
247+ if (d .redirect ) {
248+ upperredirect = kstrdup (d .redirect , GFP_KERNEL );
249+ if (!upperredirect )
250+ goto out_put_upper ;
251+ if (d .redirect [0 ] == '/' )
252+ poe = dentry -> d_sb -> s_root -> d_fsdata ;
253+ }
156254 upperopaque = d .opaque ;
157255 }
158256
159257 if (!d .stop && poe -> numlower ) {
160258 err = - ENOMEM ;
161- stack = kcalloc (poe -> numlower , sizeof (struct path ),
259+ stack = kcalloc (ofs -> numlower , sizeof (struct path ),
162260 GFP_TEMPORARY );
163261 if (!stack )
164262 goto out_put_upper ;
@@ -178,6 +276,22 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
178276 stack [ctr ].dentry = this ;
179277 stack [ctr ].mnt = lowerpath .mnt ;
180278 ctr ++ ;
279+
280+ if (d .stop )
281+ break ;
282+
283+ if (d .redirect &&
284+ d .redirect [0 ] == '/' &&
285+ poe != dentry -> d_sb -> s_root -> d_fsdata ) {
286+ poe = dentry -> d_sb -> s_root -> d_fsdata ;
287+
288+ /* Find the current layer on the root dentry */
289+ for (i = 0 ; i < poe -> numlower ; i ++ )
290+ if (poe -> lowerstack [i ].mnt == lowerpath .mnt )
291+ break ;
292+ if (WARN_ON (i == poe -> numlower ))
293+ break ;
294+ }
181295 }
182296
183297 oe = ovl_alloc_entry (ctr );
@@ -208,9 +322,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
208322
209323 revert_creds (old_cred );
210324 oe -> opaque = upperopaque ;
325+ oe -> redirect = upperredirect ;
211326 oe -> __upperdentry = upperdentry ;
212327 memcpy (oe -> lowerstack , stack , sizeof (struct path ) * ctr );
213328 kfree (stack );
329+ kfree (d .redirect );
214330 dentry -> d_fsdata = oe ;
215331 d_add (dentry , inode );
216332
@@ -224,7 +340,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
224340 kfree (stack );
225341out_put_upper :
226342 dput (upperdentry );
343+ kfree (upperredirect );
227344out :
345+ kfree (d .redirect );
228346 revert_creds (old_cred );
229347 return ERR_PTR (err );
230348}
0 commit comments