Whamcloud - gitweb
LU-15535 llite: deadlock on lli_lsm_sem
[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 (lmv_dir_foreign(lmd->lsm_obj)) {
119                         ll_manage_foreign_dir(inode, &lmd->lsm_obj->lso_lfm);
120                 } else {
121                         struct ll_inode_info *lli = ll_i2info(inode);
122                         struct lmv_stripe_object *lsm_obj;
123
124                         down_read(&lli->lli_lsm_sem);
125                         lsm_obj = lli->lli_lsm_obj;
126                         if (lmv_dir_foreign(lsm_obj))
127                                 ll_manage_foreign_dir(inode, &lsm_obj->lso_lfm);
128                         up_read(&lli->lli_lsm_sem);
129                 }
130         }
131 out:
132         RETURN(rc);
133 }
134
135 /* dentry must be spliced to inode (dentry->d_inode != NULL) !!! */
136 bool ll_foreign_is_openable(struct dentry *dentry, unsigned int flags)
137 {
138         /* check for faked symlink here as they should not be opened (unless
139          * O_NOFOLLOW!) and thus wants ll_atomic_open() to return 1 from
140          * finish_no_open() in order to get follow_link() to be called in both
141          * path_lookupat() and path_openupat().
142          * This will not break regular symlink handling as they have
143          * been treated/filtered upstream.
144          */
145         if (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode) &&
146             !(flags & O_NOFOLLOW))
147                 return false;
148
149         return true;
150 }
151
152 static bool should_preserve_foreign_file(struct lov_foreign_md *lfm,
153                                          struct ll_inode_info *lli, bool unset)
154 {
155         /* for now, only avoid foreign fake symlink file removal */
156
157         if (unset)
158                 if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
159                         set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
160                         return true;
161                 } else {
162                         return false;
163                 }
164         else
165                 return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
166                         !test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
167 }
168
169 static bool should_preserve_foreign_dir(struct lmv_foreign_md *lfm,
170                                         struct ll_inode_info *lli, bool unset)
171 {
172         /* for now, only avoid foreign fake symlink dir removal */
173
174         if (unset)
175                 if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
176                         set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
177                         return true;
178                 } else {
179                         return false;
180                 }
181         else
182                 return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
183                         !test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
184 }
185
186 /* XXX
187  * instead of fetching type from foreign LOV/LMV, we may simply
188  * check (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode))
189  * to identify a fake symlink
190  */
191 bool ll_foreign_is_removable(struct dentry *dentry, bool unset)
192 {
193         struct inode *inode = dentry->d_inode;
194         struct qstr *name = &dentry->d_name;
195         bool preserve_foreign = false;
196         int rc = 0;
197
198         ENTRY;
199         if (inode == NULL)
200                 return 0;
201
202         /* some foreign types may not be allowed to be unlinked in order to
203          * keep references with external objects
204          */
205         if (S_ISREG(inode->i_mode)) {
206                 struct ll_inode_info *lli = ll_i2info(inode);
207                 struct cl_object *obj = lli->lli_clob;
208
209                 if (obj) {
210                         struct lov_foreign_md lfm = {
211                                 .lfm_magic = LOV_MAGIC,
212                         };
213                         struct cl_layout cl = {
214                                 .cl_buf.lb_buf = &lfm,
215                                 .cl_buf.lb_len = sizeof(lfm),
216                         };
217                         struct lu_env *env;
218                         u16 refcheck;
219
220                         env = cl_env_get(&refcheck);
221                         if (IS_ERR(env))
222                                 GOTO(out, rc = PTR_ERR(env));
223                         rc = cl_object_layout_get(env, obj, &cl);
224                         /* error is likely to be -ERANGE because of the small
225                          * buffer we use, only the content is significant here
226                          */
227                         if (rc < 0 && rc != -ERANGE) {
228                                 cl_env_put(env, &refcheck);
229                                 goto out;
230                         } else {
231                                 rc = 0;
232                         }
233                         if (lfm.lfm_magic == LOV_MAGIC_FOREIGN)
234                                 preserve_foreign =
235                                         should_preserve_foreign_file(&lfm, lli,
236                                                                      unset);
237                         cl_env_put(env, &refcheck);
238                         if (preserve_foreign) {
239                                 CDEBUG(D_INFO,
240                                        "%s unlink of foreign file (%.*s, "DFID")\n",
241                                        unset ? "allow" : "prevent",
242                                        name->len, name->name,
243                                        PFID(ll_inode2fid(inode)));
244                                 RETURN(false);
245                         }
246                 } else {
247                         CDEBUG(D_INFO,
248                                "unable to check if file (%.*s, "DFID") is foreign...\n",
249                                name->len, name->name,
250                                PFID(ll_inode2fid(inode)));
251                         /* XXX should we prevent removal ?? */
252                 }
253         } else if (S_ISDIR(inode->i_mode)) {
254                 struct ll_inode_info *lli = ll_i2info(inode);
255                 struct lmv_stripe_object *lsm_obj;
256
257                 down_read(&lli->lli_lsm_sem);
258                 lsm_obj = lli->lli_lsm_obj;
259                 if (!lsm_obj)
260                         CDEBUG(D_INFO,
261                                "unable to check if dir (%.*s, "DFID") is foreign...\n",
262                                name->len, name->name,
263                                PFID(ll_inode2fid(inode)));
264                 else if (lmv_dir_foreign(lsm_obj))
265                         preserve_foreign =
266                                 should_preserve_foreign_dir(&lsm_obj->lso_lfm,
267                                                             lli, 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 }