Whamcloud - gitweb
LU-14644 vvp: wait for nrpages to be updated
[fs/lustre-release.git] / lustre / llite / llite_foreign.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2020 Intel Corporation.
24  */
25 #define DEBUG_SUBSYSTEM S_LLITE
26
27 #include "llite_internal.h"
28
29 static void ll_manage_foreign_file(struct inode *inode,
30                                    struct lov_foreign_md *lfm)
31 {
32         struct ll_sb_info *sbi = ll_i2sbi(inode);
33
34         if (le32_to_cpu(lfm->lfm_type) == LU_FOREIGN_TYPE_SYMLINK) {
35                 CDEBUG(D_INFO,
36                        "%s: inode %p of fid "DFID": Foreign file of type symlink, faking a symlink\n",
37                        sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)));
38                 /* change inode_operations to add symlink methods, and clear
39                  * IOP_NOFOLLOW to ensure file will be treated as a symlink
40                  * by Kernel (see in * d_flags_for_inode()).
41                  */
42                 inode->i_op = &ll_foreign_file_symlink_inode_operations;
43                 inode->i_opflags &= ~IOP_NOFOLLOW;
44         } else {
45                 CDEBUG(D_INFO,
46                        "%s: inode %p of fid "DFID": Foreign file of type %ux, nothing special to do\n",
47                        sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)),
48                        le32_to_cpu(lfm->lfm_type));
49         }
50 }
51
52 static void ll_manage_foreign_dir(struct inode *inode,
53                                   struct lmv_foreign_md *lfm)
54 {
55         struct ll_sb_info *sbi = ll_i2sbi(inode);
56
57         if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
58                 CDEBUG(D_INFO,
59                        "%s: inode %p of fid "DFID": Foreign dir of type symlink, faking a symlink\n",
60                        sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)));
61                 /* change inode_operations to add symlink methods
62                  * IOP_NOFOLLOW should not be set for dirs
63                  */
64                 inode->i_op = &ll_foreign_dir_symlink_inode_operations;
65         } else {
66                 CDEBUG(D_INFO,
67                        "%s: inode %p of fid "DFID": Foreign dir of type %ux, nothing special to do\n",
68                        sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)),
69                        le32_to_cpu(lfm->lfm_type));
70         }
71 }
72
73 int ll_manage_foreign(struct inode *inode, struct lustre_md *lmd)
74 {
75         int rc = 0;
76
77         ENTRY;
78         /* apply any foreign file/dir policy */
79         if (S_ISREG((inode)->i_mode)) {
80                 struct ll_inode_info *lli = ll_i2info(inode);
81                 struct cl_object *obj = lli->lli_clob;
82
83                 if (lmd->layout.lb_buf != NULL && lmd->layout.lb_len != 0) {
84                         struct lov_foreign_md *lfm = lmd->layout.lb_buf;
85
86                         if (lfm->lfm_magic == LOV_MAGIC_FOREIGN)
87                                 ll_manage_foreign_file(inode, lfm);
88                         GOTO(out, rc);
89                 }
90
91                 if (obj) {
92                         struct lov_foreign_md lfm = {
93                                 .lfm_magic = LOV_MAGIC,
94                         };
95                         struct cl_layout cl = {
96                                 .cl_buf.lb_buf = &lfm,
97                                 .cl_buf.lb_len = sizeof(lfm),
98                         };
99                         struct lu_env *env;
100                         u16 refcheck;
101
102                         env = cl_env_get(&refcheck);
103                         if (IS_ERR(env))
104                                 GOTO(out, rc = PTR_ERR(env));
105                         rc = cl_object_layout_get(env, obj, &cl);
106                         /* error is likely to be -ERANGE because of the small
107                          * buffer we use, only the content is significant here
108                          */
109                         if (rc < 0 && rc != -ERANGE) {
110                                 cl_env_put(env, &refcheck);
111                                 GOTO(out, rc);
112                         }
113                         if (lfm.lfm_magic == LOV_MAGIC_FOREIGN)
114                                 ll_manage_foreign_file(inode, &lfm);
115                         cl_env_put(env, &refcheck);
116                 }
117         } else if (S_ISDIR((inode)->i_mode)) {
118                 if (lmd->lfm != NULL &&
119                     lmd->lfm->lfm_magic == LMV_MAGIC_FOREIGN) {
120                         ll_manage_foreign_dir(inode, lmd->lfm);
121                 } else {
122                         struct ll_inode_info *lli = ll_i2info(inode);
123                         struct lmv_foreign_md *lfm;
124
125                         down_read(&lli->lli_lsm_sem);
126                         lfm = (struct lmv_foreign_md *)(lli->lli_lsm_md);
127                         if (lfm &&  lfm->lfm_magic == LMV_MAGIC_FOREIGN)
128                                 ll_manage_foreign_dir(inode, lfm);
129                         up_read(&lli->lli_lsm_sem);
130                 }
131         }
132 out:
133         RETURN(rc);
134 }
135
136 /* dentry must be spliced to inode (dentry->d_inode != NULL) !!! */
137 bool ll_foreign_is_openable(struct dentry *dentry, unsigned int flags)
138 {
139         /* check for faked symlink here as they should not be opened (unless
140          * O_NOFOLLOW!) and thus wants ll_atomic_open() to return 1 from
141          * finish_no_open() in order to get follow_link() to be called in both
142          * path_lookupat() and path_openupat().
143          * This will not break regular symlink handling as they have
144          * been treated/filtered upstream.
145          */
146         if (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode) &&
147             !(flags & O_NOFOLLOW))
148                 return false;
149
150         return true;
151 }
152
153 static bool should_preserve_foreign_file(struct lov_foreign_md *lfm,
154                                          struct ll_inode_info *lli, bool unset)
155 {
156         /* for now, only avoid foreign fake symlink file removal */
157
158         if (unset)
159                 if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
160                         set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
161                         return true;
162                 } else {
163                         return false;
164                 }
165         else
166                 return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
167                         !test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
168 }
169
170 static bool should_preserve_foreign_dir(struct lmv_foreign_md *lfm,
171                                         struct ll_inode_info *lli, bool unset)
172 {
173         /* for now, only avoid foreign fake symlink dir removal */
174
175         if (unset)
176                 if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
177                         set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
178                         return true;
179                 } else {
180                         return false;
181                 }
182         else
183                 return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
184                         !test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
185 }
186
187 /* XXX
188  * instead of fetching type from foreign LOV/LMV, we may simply
189  * check (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode))
190  * to identify a fake symlink
191  */
192 bool ll_foreign_is_removable(struct dentry *dentry, bool unset)
193 {
194         struct inode *inode = dentry->d_inode;
195         struct qstr *name = &dentry->d_name;
196         bool preserve_foreign = false;
197         int rc = 0;
198
199         ENTRY;
200         if (inode == NULL)
201                 return 0;
202
203         /* some foreign types may not be allowed to be unlinked in order to
204          * keep references with external objects
205          */
206         if (S_ISREG(inode->i_mode)) {
207                 struct ll_inode_info *lli = ll_i2info(inode);
208                 struct cl_object *obj = lli->lli_clob;
209
210                 if (obj) {
211                         struct lov_foreign_md lfm = {
212                                 .lfm_magic = LOV_MAGIC,
213                         };
214                         struct cl_layout cl = {
215                                 .cl_buf.lb_buf = &lfm,
216                                 .cl_buf.lb_len = sizeof(lfm),
217                         };
218                         struct lu_env *env;
219                         u16 refcheck;
220
221                         env = cl_env_get(&refcheck);
222                         if (IS_ERR(env))
223                                 GOTO(out, rc = PTR_ERR(env));
224                         rc = cl_object_layout_get(env, obj, &cl);
225                         /* error is likely to be -ERANGE because of the small
226                          * buffer we use, only the content is significant here
227                          */
228                         if (rc < 0 && rc != -ERANGE) {
229                                 cl_env_put(env, &refcheck);
230                                 goto out;
231                         } else {
232                                 rc = 0;
233                         }
234                         if (lfm.lfm_magic == LOV_MAGIC_FOREIGN)
235                                 preserve_foreign =
236                                         should_preserve_foreign_file(&lfm, lli,
237                                                                      unset);
238                         cl_env_put(env, &refcheck);
239                         if (preserve_foreign) {
240                                 CDEBUG(D_INFO,
241                                        "%s unlink of foreign file (%.*s, "DFID")\n",
242                                        unset ? "allow" : "prevent",
243                                        name->len, name->name,
244                                        PFID(ll_inode2fid(inode)));
245                                 RETURN(false);
246                         }
247                 } else {
248                         CDEBUG(D_INFO,
249                                "unable to check if file (%.*s, "DFID") is foreign...\n",
250                                name->len, name->name,
251                                PFID(ll_inode2fid(inode)));
252                         /* XXX should we prevent removal ?? */
253                 }
254         } else if (S_ISDIR(inode->i_mode)) {
255                 struct ll_inode_info *lli = ll_i2info(inode);
256                 struct lmv_foreign_md *lfm;
257
258                 down_read(&lli->lli_lsm_sem);
259                 lfm = (struct lmv_foreign_md *)(lli->lli_lsm_md);
260                 if (!lfm)
261                         CDEBUG(D_INFO,
262                                "unable to check if dir (%.*s, "DFID") is foreign...\n",
263                                name->len, name->name,
264                                PFID(ll_inode2fid(inode)));
265                 else if (lfm->lfm_magic == LMV_MAGIC_FOREIGN)
266                         preserve_foreign = should_preserve_foreign_dir(lfm, lli,
267                                                                        unset);
268                 up_read(&lli->lli_lsm_sem);
269                 if (preserve_foreign) {
270                         CDEBUG(D_INFO,
271                                "%s unlink of foreign dir (%.*s, "DFID")\n",
272                                unset ? "allow" : "prevent",
273                                name->len, name->name,
274                                PFID(ll_inode2fid(inode)));
275                         RETURN(false);
276                 }
277         }
278
279 out:
280         RETURN(true);
281 }