ovl: decode pure upper file handles
[linux-block.git] / fs / overlayfs / export.c
CommitLineData
8ed5eec9
AG
1/*
2 * Overlayfs NFS export support.
3 *
4 * Amir Goldstein <amir73il@gmail.com>
5 *
6 * Copyright (C) 2017-2018 CTERA Networks. All Rights Reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published by
10 * the Free Software Foundation.
11 */
12
13#include <linux/fs.h>
14#include <linux/cred.h>
15#include <linux/mount.h>
16#include <linux/namei.h>
17#include <linux/xattr.h>
18#include <linux/exportfs.h>
19#include <linux/ratelimit.h>
20#include "overlayfs.h"
21
22static int ovl_d_to_fh(struct dentry *dentry, char *buf, int buflen)
23{
24 struct dentry *upper = ovl_dentry_upper(dentry);
25 struct dentry *origin = ovl_dentry_lower(dentry);
26 struct ovl_fh *fh = NULL;
27 int err;
28
29 /*
30 * On overlay with an upper layer, overlay root inode is encoded as
31 * an upper file handle, because upper root dir is not indexed.
32 */
33 if (dentry == dentry->d_sb->s_root && upper)
34 origin = NULL;
35
36 err = -EACCES;
37 if (!upper || origin)
38 goto fail;
39
40 /* TODO: encode non pure-upper by origin */
41 fh = ovl_encode_fh(upper, true);
42
43 err = -EOVERFLOW;
44 if (fh->len > buflen)
45 goto fail;
46
47 memcpy(buf, (char *)fh, fh->len);
48 err = fh->len;
49
50out:
51 kfree(fh);
52 return err;
53
54fail:
55 pr_warn_ratelimited("overlayfs: failed to encode file handle (%pd2, err=%i, buflen=%d, len=%d, type=%d)\n",
56 dentry, err, buflen, fh ? (int)fh->len : 0,
57 fh ? fh->type : 0);
58 goto out;
59}
60
61static int ovl_dentry_to_fh(struct dentry *dentry, u32 *fid, int *max_len)
62{
63 int res, len = *max_len << 2;
64
65 res = ovl_d_to_fh(dentry, (char *)fid, len);
66 if (res <= 0)
67 return FILEID_INVALID;
68
69 len = res;
70
71 /* Round up to dwords */
72 *max_len = (len + 3) >> 2;
73 return OVL_FILEID;
74}
75
76static int ovl_encode_inode_fh(struct inode *inode, u32 *fid, int *max_len,
77 struct inode *parent)
78{
79 struct dentry *dentry;
80 int type;
81
82 /* TODO: encode connectable file handles */
83 if (parent)
84 return FILEID_INVALID;
85
86 dentry = d_find_any_alias(inode);
87 if (WARN_ON(!dentry))
88 return FILEID_INVALID;
89
90 type = ovl_dentry_to_fh(dentry, fid, max_len);
91
92 dput(dentry);
93 return type;
94}
95
8556a420
AG
96/*
97 * Find or instantiate an overlay dentry from real dentries.
98 */
99static struct dentry *ovl_obtain_alias(struct super_block *sb,
100 struct dentry *upper,
101 struct ovl_path *lowerpath)
102{
103 struct inode *inode;
104 struct dentry *dentry;
105 struct ovl_entry *oe;
106 void *fsdata = &oe;
107
108 /* TODO: obtain non pure-upper */
109 if (lowerpath)
110 return ERR_PTR(-EIO);
111
112 inode = ovl_get_inode(sb, dget(upper), NULL, NULL, 0);
113 if (IS_ERR(inode)) {
114 dput(upper);
115 return ERR_CAST(inode);
116 }
117
118 dentry = d_find_any_alias(inode);
119 if (!dentry) {
120 dentry = d_alloc_anon(inode->i_sb);
121 if (!dentry)
122 goto nomem;
123 oe = ovl_alloc_entry(0);
124 if (!oe)
125 goto nomem;
126
127 dentry->d_fsdata = oe;
128 ovl_dentry_set_upper_alias(dentry);
129 }
130
131 return d_instantiate_anon(dentry, inode);
132
133nomem:
134 iput(inode);
135 dput(dentry);
136 return ERR_PTR(-ENOMEM);
137}
138
139static struct dentry *ovl_upper_fh_to_d(struct super_block *sb,
140 struct ovl_fh *fh)
141{
142 struct ovl_fs *ofs = sb->s_fs_info;
143 struct dentry *dentry;
144 struct dentry *upper;
145
146 if (!ofs->upper_mnt)
147 return ERR_PTR(-EACCES);
148
149 upper = ovl_decode_fh(fh, ofs->upper_mnt);
150 if (IS_ERR_OR_NULL(upper))
151 return upper;
152
153 dentry = ovl_obtain_alias(sb, upper, NULL);
154 dput(upper);
155
156 return dentry;
157}
158
159static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid,
160 int fh_len, int fh_type)
161{
162 struct dentry *dentry = NULL;
163 struct ovl_fh *fh = (struct ovl_fh *) fid;
164 int len = fh_len << 2;
165 unsigned int flags = 0;
166 int err;
167
168 err = -EINVAL;
169 if (fh_type != OVL_FILEID)
170 goto out_err;
171
172 err = ovl_check_fh_len(fh, len);
173 if (err)
174 goto out_err;
175
176 /* TODO: decode non-upper */
177 flags = fh->flags;
178 if (flags & OVL_FH_FLAG_PATH_UPPER)
179 dentry = ovl_upper_fh_to_d(sb, fh);
180 err = PTR_ERR(dentry);
181 if (IS_ERR(dentry) && err != -ESTALE)
182 goto out_err;
183
184 return dentry;
185
186out_err:
187 pr_warn_ratelimited("overlayfs: failed to decode file handle (len=%d, type=%d, flags=%x, err=%i)\n",
188 len, fh_type, flags, err);
189 return ERR_PTR(err);
190}
191
8ed5eec9
AG
192const struct export_operations ovl_export_operations = {
193 .encode_fh = ovl_encode_inode_fh,
8556a420 194 .fh_to_dentry = ovl_fh_to_dentry,
8ed5eec9 195};