diff options
Diffstat (limited to 'fs/orangefs/orangefs-utils.c')
-rw-r--r-- | fs/orangefs/orangefs-utils.c | 1048 |
1 files changed, 1048 insertions, 0 deletions
diff --git a/fs/orangefs/orangefs-utils.c b/fs/orangefs/orangefs-utils.c new file mode 100644 index 000000000000..40f5163b56aa --- /dev/null +++ b/fs/orangefs/orangefs-utils.c | |||
@@ -0,0 +1,1048 @@ | |||
1 | /* | ||
2 | * (C) 2001 Clemson University and The University of Chicago | ||
3 | * | ||
4 | * See COPYING in top-level directory. | ||
5 | */ | ||
6 | #include "protocol.h" | ||
7 | #include "orangefs-kernel.h" | ||
8 | #include "orangefs-dev-proto.h" | ||
9 | #include "orangefs-bufmap.h" | ||
10 | |||
11 | __s32 fsid_of_op(struct orangefs_kernel_op_s *op) | ||
12 | { | ||
13 | __s32 fsid = ORANGEFS_FS_ID_NULL; | ||
14 | |||
15 | if (op) { | ||
16 | switch (op->upcall.type) { | ||
17 | case ORANGEFS_VFS_OP_FILE_IO: | ||
18 | fsid = op->upcall.req.io.refn.fs_id; | ||
19 | break; | ||
20 | case ORANGEFS_VFS_OP_LOOKUP: | ||
21 | fsid = op->upcall.req.lookup.parent_refn.fs_id; | ||
22 | break; | ||
23 | case ORANGEFS_VFS_OP_CREATE: | ||
24 | fsid = op->upcall.req.create.parent_refn.fs_id; | ||
25 | break; | ||
26 | case ORANGEFS_VFS_OP_GETATTR: | ||
27 | fsid = op->upcall.req.getattr.refn.fs_id; | ||
28 | break; | ||
29 | case ORANGEFS_VFS_OP_REMOVE: | ||
30 | fsid = op->upcall.req.remove.parent_refn.fs_id; | ||
31 | break; | ||
32 | case ORANGEFS_VFS_OP_MKDIR: | ||
33 | fsid = op->upcall.req.mkdir.parent_refn.fs_id; | ||
34 | break; | ||
35 | case ORANGEFS_VFS_OP_READDIR: | ||
36 | fsid = op->upcall.req.readdir.refn.fs_id; | ||
37 | break; | ||
38 | case ORANGEFS_VFS_OP_SETATTR: | ||
39 | fsid = op->upcall.req.setattr.refn.fs_id; | ||
40 | break; | ||
41 | case ORANGEFS_VFS_OP_SYMLINK: | ||
42 | fsid = op->upcall.req.sym.parent_refn.fs_id; | ||
43 | break; | ||
44 | case ORANGEFS_VFS_OP_RENAME: | ||
45 | fsid = op->upcall.req.rename.old_parent_refn.fs_id; | ||
46 | break; | ||
47 | case ORANGEFS_VFS_OP_STATFS: | ||
48 | fsid = op->upcall.req.statfs.fs_id; | ||
49 | break; | ||
50 | case ORANGEFS_VFS_OP_TRUNCATE: | ||
51 | fsid = op->upcall.req.truncate.refn.fs_id; | ||
52 | break; | ||
53 | case ORANGEFS_VFS_OP_MMAP_RA_FLUSH: | ||
54 | fsid = op->upcall.req.ra_cache_flush.refn.fs_id; | ||
55 | break; | ||
56 | case ORANGEFS_VFS_OP_FS_UMOUNT: | ||
57 | fsid = op->upcall.req.fs_umount.fs_id; | ||
58 | break; | ||
59 | case ORANGEFS_VFS_OP_GETXATTR: | ||
60 | fsid = op->upcall.req.getxattr.refn.fs_id; | ||
61 | break; | ||
62 | case ORANGEFS_VFS_OP_SETXATTR: | ||
63 | fsid = op->upcall.req.setxattr.refn.fs_id; | ||
64 | break; | ||
65 | case ORANGEFS_VFS_OP_LISTXATTR: | ||
66 | fsid = op->upcall.req.listxattr.refn.fs_id; | ||
67 | break; | ||
68 | case ORANGEFS_VFS_OP_REMOVEXATTR: | ||
69 | fsid = op->upcall.req.removexattr.refn.fs_id; | ||
70 | break; | ||
71 | case ORANGEFS_VFS_OP_FSYNC: | ||
72 | fsid = op->upcall.req.fsync.refn.fs_id; | ||
73 | break; | ||
74 | default: | ||
75 | break; | ||
76 | } | ||
77 | } | ||
78 | return fsid; | ||
79 | } | ||
80 | |||
81 | static int orangefs_inode_flags(struct ORANGEFS_sys_attr_s *attrs) | ||
82 | { | ||
83 | int flags = 0; | ||
84 | if (attrs->flags & ORANGEFS_IMMUTABLE_FL) | ||
85 | flags |= S_IMMUTABLE; | ||
86 | else | ||
87 | flags &= ~S_IMMUTABLE; | ||
88 | if (attrs->flags & ORANGEFS_APPEND_FL) | ||
89 | flags |= S_APPEND; | ||
90 | else | ||
91 | flags &= ~S_APPEND; | ||
92 | if (attrs->flags & ORANGEFS_NOATIME_FL) | ||
93 | flags |= S_NOATIME; | ||
94 | else | ||
95 | flags &= ~S_NOATIME; | ||
96 | return flags; | ||
97 | } | ||
98 | |||
99 | static int orangefs_inode_perms(struct ORANGEFS_sys_attr_s *attrs) | ||
100 | { | ||
101 | int perm_mode = 0; | ||
102 | |||
103 | if (attrs->perms & ORANGEFS_O_EXECUTE) | ||
104 | perm_mode |= S_IXOTH; | ||
105 | if (attrs->perms & ORANGEFS_O_WRITE) | ||
106 | perm_mode |= S_IWOTH; | ||
107 | if (attrs->perms & ORANGEFS_O_READ) | ||
108 | perm_mode |= S_IROTH; | ||
109 | |||
110 | if (attrs->perms & ORANGEFS_G_EXECUTE) | ||
111 | perm_mode |= S_IXGRP; | ||
112 | if (attrs->perms & ORANGEFS_G_WRITE) | ||
113 | perm_mode |= S_IWGRP; | ||
114 | if (attrs->perms & ORANGEFS_G_READ) | ||
115 | perm_mode |= S_IRGRP; | ||
116 | |||
117 | if (attrs->perms & ORANGEFS_U_EXECUTE) | ||
118 | perm_mode |= S_IXUSR; | ||
119 | if (attrs->perms & ORANGEFS_U_WRITE) | ||
120 | perm_mode |= S_IWUSR; | ||
121 | if (attrs->perms & ORANGEFS_U_READ) | ||
122 | perm_mode |= S_IRUSR; | ||
123 | |||
124 | if (attrs->perms & ORANGEFS_G_SGID) | ||
125 | perm_mode |= S_ISGID; | ||
126 | if (attrs->perms & ORANGEFS_U_SUID) | ||
127 | perm_mode |= S_ISUID; | ||
128 | |||
129 | return perm_mode; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * NOTE: in kernel land, we never use the sys_attr->link_target for | ||
134 | * anything, so don't bother copying it into the sys_attr object here. | ||
135 | */ | ||
136 | static inline int copy_attributes_from_inode(struct inode *inode, | ||
137 | struct ORANGEFS_sys_attr_s *attrs, | ||
138 | struct iattr *iattr) | ||
139 | { | ||
140 | umode_t tmp_mode; | ||
141 | |||
142 | if (!iattr || !inode || !attrs) { | ||
143 | gossip_err("NULL iattr (%p), inode (%p), attrs (%p) " | ||
144 | "in copy_attributes_from_inode!\n", | ||
145 | iattr, | ||
146 | inode, | ||
147 | attrs); | ||
148 | return -EINVAL; | ||
149 | } | ||
150 | /* | ||
151 | * We need to be careful to only copy the attributes out of the | ||
152 | * iattr object that we know are valid. | ||
153 | */ | ||
154 | attrs->mask = 0; | ||
155 | if (iattr->ia_valid & ATTR_UID) { | ||
156 | attrs->owner = from_kuid(current_user_ns(), iattr->ia_uid); | ||
157 | attrs->mask |= ORANGEFS_ATTR_SYS_UID; | ||
158 | gossip_debug(GOSSIP_UTILS_DEBUG, "(UID) %d\n", attrs->owner); | ||
159 | } | ||
160 | if (iattr->ia_valid & ATTR_GID) { | ||
161 | attrs->group = from_kgid(current_user_ns(), iattr->ia_gid); | ||
162 | attrs->mask |= ORANGEFS_ATTR_SYS_GID; | ||
163 | gossip_debug(GOSSIP_UTILS_DEBUG, "(GID) %d\n", attrs->group); | ||
164 | } | ||
165 | |||
166 | if (iattr->ia_valid & ATTR_ATIME) { | ||
167 | attrs->mask |= ORANGEFS_ATTR_SYS_ATIME; | ||
168 | if (iattr->ia_valid & ATTR_ATIME_SET) { | ||
169 | attrs->atime = (time64_t)iattr->ia_atime.tv_sec; | ||
170 | attrs->mask |= ORANGEFS_ATTR_SYS_ATIME_SET; | ||
171 | } | ||
172 | } | ||
173 | if (iattr->ia_valid & ATTR_MTIME) { | ||
174 | attrs->mask |= ORANGEFS_ATTR_SYS_MTIME; | ||
175 | if (iattr->ia_valid & ATTR_MTIME_SET) { | ||
176 | attrs->mtime = (time64_t)iattr->ia_mtime.tv_sec; | ||
177 | attrs->mask |= ORANGEFS_ATTR_SYS_MTIME_SET; | ||
178 | } | ||
179 | } | ||
180 | if (iattr->ia_valid & ATTR_CTIME) | ||
181 | attrs->mask |= ORANGEFS_ATTR_SYS_CTIME; | ||
182 | |||
183 | /* | ||
184 | * ORANGEFS cannot set size with a setattr operation. Probably not likely | ||
185 | * to be requested through the VFS, but just in case, don't worry about | ||
186 | * ATTR_SIZE | ||
187 | */ | ||
188 | |||
189 | if (iattr->ia_valid & ATTR_MODE) { | ||
190 | tmp_mode = iattr->ia_mode; | ||
191 | if (tmp_mode & (S_ISVTX)) { | ||
192 | if (is_root_handle(inode)) { | ||
193 | /* | ||
194 | * allow sticky bit to be set on root (since | ||
195 | * it shows up that way by default anyhow), | ||
196 | * but don't show it to the server | ||
197 | */ | ||
198 | tmp_mode -= S_ISVTX; | ||
199 | } else { | ||
200 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
201 | "User attempted to set sticky bit on non-root directory; returning EINVAL.\n"); | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | if (tmp_mode & (S_ISUID)) { | ||
207 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
208 | "Attempting to set setuid bit (not supported); returning EINVAL.\n"); | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | |||
212 | attrs->perms = ORANGEFS_util_translate_mode(tmp_mode); | ||
213 | attrs->mask |= ORANGEFS_ATTR_SYS_PERM; | ||
214 | } | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int orangefs_inode_type(enum orangefs_ds_type objtype) | ||
220 | { | ||
221 | if (objtype == ORANGEFS_TYPE_METAFILE) | ||
222 | return S_IFREG; | ||
223 | else if (objtype == ORANGEFS_TYPE_DIRECTORY) | ||
224 | return S_IFDIR; | ||
225 | else if (objtype == ORANGEFS_TYPE_SYMLINK) | ||
226 | return S_IFLNK; | ||
227 | else | ||
228 | return -1; | ||
229 | } | ||
230 | |||
231 | static int orangefs_inode_is_stale(struct inode *inode, int new, | ||
232 | struct ORANGEFS_sys_attr_s *attrs, char *link_target) | ||
233 | { | ||
234 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
235 | int type = orangefs_inode_type(attrs->objtype); | ||
236 | if (!new) { | ||
237 | /* | ||
238 | * If the inode type or symlink target have changed then this | ||
239 | * inode is stale. | ||
240 | */ | ||
241 | if (type == -1 || !(inode->i_mode & type)) { | ||
242 | orangefs_make_bad_inode(inode); | ||
243 | return 1; | ||
244 | } | ||
245 | if (type == S_IFLNK && strncmp(orangefs_inode->link_target, | ||
246 | link_target, ORANGEFS_NAME_MAX)) { | ||
247 | orangefs_make_bad_inode(inode); | ||
248 | return 1; | ||
249 | } | ||
250 | } | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | int orangefs_inode_getattr(struct inode *inode, int new, int size) | ||
255 | { | ||
256 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
257 | struct orangefs_kernel_op_s *new_op; | ||
258 | loff_t inode_size, rounded_up_size; | ||
259 | int ret, type; | ||
260 | |||
261 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: called on inode %pU\n", __func__, | ||
262 | get_khandle_from_ino(inode)); | ||
263 | |||
264 | new_op = op_alloc(ORANGEFS_VFS_OP_GETATTR); | ||
265 | if (!new_op) | ||
266 | return -ENOMEM; | ||
267 | new_op->upcall.req.getattr.refn = orangefs_inode->refn; | ||
268 | new_op->upcall.req.getattr.mask = size ? | ||
269 | ORANGEFS_ATTR_SYS_ALL_NOHINT : ORANGEFS_ATTR_SYS_ALL_NOHINT_NOSIZE; | ||
270 | |||
271 | ret = service_operation(new_op, __func__, | ||
272 | get_interruptible_flag(inode)); | ||
273 | if (ret != 0) | ||
274 | goto out; | ||
275 | |||
276 | type = orangefs_inode_type(new_op-> | ||
277 | downcall.resp.getattr.attributes.objtype); | ||
278 | ret = orangefs_inode_is_stale(inode, new, | ||
279 | &new_op->downcall.resp.getattr.attributes, | ||
280 | new_op->downcall.resp.getattr.link_target); | ||
281 | if (ret) { | ||
282 | ret = -ESTALE; | ||
283 | goto out; | ||
284 | } | ||
285 | |||
286 | switch (type) { | ||
287 | case S_IFREG: | ||
288 | inode->i_flags = orangefs_inode_flags(&new_op-> | ||
289 | downcall.resp.getattr.attributes); | ||
290 | if (size) { | ||
291 | inode_size = (loff_t)new_op-> | ||
292 | downcall.resp.getattr.attributes.size; | ||
293 | rounded_up_size = | ||
294 | (inode_size + (4096 - (inode_size % 4096))); | ||
295 | inode->i_size = inode_size; | ||
296 | orangefs_inode->blksize = | ||
297 | new_op->downcall.resp.getattr.attributes.blksize; | ||
298 | spin_lock(&inode->i_lock); | ||
299 | inode->i_bytes = inode_size; | ||
300 | inode->i_blocks = | ||
301 | (unsigned long)(rounded_up_size / 512); | ||
302 | spin_unlock(&inode->i_lock); | ||
303 | } | ||
304 | break; | ||
305 | case S_IFDIR: | ||
306 | inode->i_size = PAGE_CACHE_SIZE; | ||
307 | orangefs_inode->blksize = (1 << inode->i_blkbits); | ||
308 | spin_lock(&inode->i_lock); | ||
309 | inode_set_bytes(inode, inode->i_size); | ||
310 | spin_unlock(&inode->i_lock); | ||
311 | set_nlink(inode, 1); | ||
312 | break; | ||
313 | case S_IFLNK: | ||
314 | if (new) { | ||
315 | inode->i_size = (loff_t)strlen(new_op-> | ||
316 | downcall.resp.getattr.link_target); | ||
317 | orangefs_inode->blksize = (1 << inode->i_blkbits); | ||
318 | strlcpy(orangefs_inode->link_target, | ||
319 | new_op->downcall.resp.getattr.link_target, | ||
320 | ORANGEFS_NAME_MAX); | ||
321 | inode->i_link = orangefs_inode->link_target; | ||
322 | } | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | inode->i_uid = make_kuid(&init_user_ns, new_op-> | ||
327 | downcall.resp.getattr.attributes.owner); | ||
328 | inode->i_gid = make_kgid(&init_user_ns, new_op-> | ||
329 | downcall.resp.getattr.attributes.group); | ||
330 | inode->i_atime.tv_sec = (time64_t)new_op-> | ||
331 | downcall.resp.getattr.attributes.atime; | ||
332 | inode->i_mtime.tv_sec = (time64_t)new_op-> | ||
333 | downcall.resp.getattr.attributes.mtime; | ||
334 | inode->i_ctime.tv_sec = (time64_t)new_op-> | ||
335 | downcall.resp.getattr.attributes.ctime; | ||
336 | inode->i_atime.tv_nsec = 0; | ||
337 | inode->i_mtime.tv_nsec = 0; | ||
338 | inode->i_ctime.tv_nsec = 0; | ||
339 | |||
340 | /* special case: mark the root inode as sticky */ | ||
341 | inode->i_mode = type | (is_root_handle(inode) ? S_ISVTX : 0) | | ||
342 | orangefs_inode_perms(&new_op->downcall.resp.getattr.attributes); | ||
343 | |||
344 | ret = 0; | ||
345 | out: | ||
346 | op_release(new_op); | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | int orangefs_inode_check_changed(struct inode *inode) | ||
351 | { | ||
352 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
353 | struct orangefs_kernel_op_s *new_op; | ||
354 | int ret; | ||
355 | |||
356 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: called on inode %pU\n", __func__, | ||
357 | get_khandle_from_ino(inode)); | ||
358 | |||
359 | new_op = op_alloc(ORANGEFS_VFS_OP_GETATTR); | ||
360 | if (!new_op) | ||
361 | return -ENOMEM; | ||
362 | new_op->upcall.req.getattr.refn = orangefs_inode->refn; | ||
363 | new_op->upcall.req.getattr.mask = ORANGEFS_ATTR_SYS_TYPE | | ||
364 | ORANGEFS_ATTR_SYS_LNK_TARGET; | ||
365 | |||
366 | ret = service_operation(new_op, __func__, | ||
367 | get_interruptible_flag(inode)); | ||
368 | if (ret != 0) | ||
369 | goto out; | ||
370 | |||
371 | ret = orangefs_inode_is_stale(inode, 0, | ||
372 | &new_op->downcall.resp.getattr.attributes, | ||
373 | new_op->downcall.resp.getattr.link_target); | ||
374 | out: | ||
375 | op_release(new_op); | ||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * issues a orangefs setattr request to make sure the new attribute values | ||
381 | * take effect if successful. returns 0 on success; -errno otherwise | ||
382 | */ | ||
383 | int orangefs_inode_setattr(struct inode *inode, struct iattr *iattr) | ||
384 | { | ||
385 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
386 | struct orangefs_kernel_op_s *new_op; | ||
387 | int ret; | ||
388 | |||
389 | new_op = op_alloc(ORANGEFS_VFS_OP_SETATTR); | ||
390 | if (!new_op) | ||
391 | return -ENOMEM; | ||
392 | |||
393 | new_op->upcall.req.setattr.refn = orangefs_inode->refn; | ||
394 | ret = copy_attributes_from_inode(inode, | ||
395 | &new_op->upcall.req.setattr.attributes, | ||
396 | iattr); | ||
397 | if (ret >= 0) { | ||
398 | ret = service_operation(new_op, __func__, | ||
399 | get_interruptible_flag(inode)); | ||
400 | |||
401 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
402 | "orangefs_inode_setattr: returning %d\n", | ||
403 | ret); | ||
404 | } | ||
405 | |||
406 | op_release(new_op); | ||
407 | |||
408 | /* | ||
409 | * successful setattr should clear the atime, mtime and | ||
410 | * ctime flags. | ||
411 | */ | ||
412 | if (ret == 0) { | ||
413 | ClearAtimeFlag(orangefs_inode); | ||
414 | ClearMtimeFlag(orangefs_inode); | ||
415 | ClearCtimeFlag(orangefs_inode); | ||
416 | ClearModeFlag(orangefs_inode); | ||
417 | } | ||
418 | |||
419 | return ret; | ||
420 | } | ||
421 | |||
422 | int orangefs_flush_inode(struct inode *inode) | ||
423 | { | ||
424 | /* | ||
425 | * If it is a dirty inode, this function gets called. | ||
426 | * Gather all the information that needs to be setattr'ed | ||
427 | * Right now, this will only be used for mode, atime, mtime | ||
428 | * and/or ctime. | ||
429 | */ | ||
430 | struct iattr wbattr; | ||
431 | int ret; | ||
432 | int mtime_flag; | ||
433 | int ctime_flag; | ||
434 | int atime_flag; | ||
435 | int mode_flag; | ||
436 | struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode); | ||
437 | |||
438 | memset(&wbattr, 0, sizeof(wbattr)); | ||
439 | |||
440 | /* | ||
441 | * check inode flags up front, and clear them if they are set. This | ||
442 | * will prevent multiple processes from all trying to flush the same | ||
443 | * inode if they call close() simultaneously | ||
444 | */ | ||
445 | mtime_flag = MtimeFlag(orangefs_inode); | ||
446 | ClearMtimeFlag(orangefs_inode); | ||
447 | ctime_flag = CtimeFlag(orangefs_inode); | ||
448 | ClearCtimeFlag(orangefs_inode); | ||
449 | atime_flag = AtimeFlag(orangefs_inode); | ||
450 | ClearAtimeFlag(orangefs_inode); | ||
451 | mode_flag = ModeFlag(orangefs_inode); | ||
452 | ClearModeFlag(orangefs_inode); | ||
453 | |||
454 | /* -- Lazy atime,mtime and ctime update -- | ||
455 | * Note: all times are dictated by server in the new scheme | ||
456 | * and not by the clients | ||
457 | * | ||
458 | * Also mode updates are being handled now.. | ||
459 | */ | ||
460 | |||
461 | if (mtime_flag) | ||
462 | wbattr.ia_valid |= ATTR_MTIME; | ||
463 | if (ctime_flag) | ||
464 | wbattr.ia_valid |= ATTR_CTIME; | ||
465 | if (atime_flag) | ||
466 | wbattr.ia_valid |= ATTR_ATIME; | ||
467 | |||
468 | if (mode_flag) { | ||
469 | wbattr.ia_mode = inode->i_mode; | ||
470 | wbattr.ia_valid |= ATTR_MODE; | ||
471 | } | ||
472 | |||
473 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
474 | "*********** orangefs_flush_inode: %pU " | ||
475 | "(ia_valid %d)\n", | ||
476 | get_khandle_from_ino(inode), | ||
477 | wbattr.ia_valid); | ||
478 | if (wbattr.ia_valid == 0) { | ||
479 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
480 | "orangefs_flush_inode skipping setattr()\n"); | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
485 | "orangefs_flush_inode (%pU) writing mode %o\n", | ||
486 | get_khandle_from_ino(inode), | ||
487 | inode->i_mode); | ||
488 | |||
489 | ret = orangefs_inode_setattr(inode, &wbattr); | ||
490 | |||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | int orangefs_unmount_sb(struct super_block *sb) | ||
495 | { | ||
496 | int ret = -EINVAL; | ||
497 | struct orangefs_kernel_op_s *new_op = NULL; | ||
498 | |||
499 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
500 | "orangefs_unmount_sb called on sb %p\n", | ||
501 | sb); | ||
502 | |||
503 | new_op = op_alloc(ORANGEFS_VFS_OP_FS_UMOUNT); | ||
504 | if (!new_op) | ||
505 | return -ENOMEM; | ||
506 | new_op->upcall.req.fs_umount.id = ORANGEFS_SB(sb)->id; | ||
507 | new_op->upcall.req.fs_umount.fs_id = ORANGEFS_SB(sb)->fs_id; | ||
508 | strncpy(new_op->upcall.req.fs_umount.orangefs_config_server, | ||
509 | ORANGEFS_SB(sb)->devname, | ||
510 | ORANGEFS_MAX_SERVER_ADDR_LEN); | ||
511 | |||
512 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
513 | "Attempting ORANGEFS Unmount via host %s\n", | ||
514 | new_op->upcall.req.fs_umount.orangefs_config_server); | ||
515 | |||
516 | ret = service_operation(new_op, "orangefs_fs_umount", 0); | ||
517 | |||
518 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
519 | "orangefs_unmount: got return value of %d\n", ret); | ||
520 | if (ret) | ||
521 | sb = ERR_PTR(ret); | ||
522 | else | ||
523 | ORANGEFS_SB(sb)->mount_pending = 1; | ||
524 | |||
525 | op_release(new_op); | ||
526 | return ret; | ||
527 | } | ||
528 | |||
529 | void orangefs_make_bad_inode(struct inode *inode) | ||
530 | { | ||
531 | if (is_root_handle(inode)) { | ||
532 | /* | ||
533 | * if this occurs, the pvfs2-client-core was killed but we | ||
534 | * can't afford to lose the inode operations and such | ||
535 | * associated with the root handle in any case. | ||
536 | */ | ||
537 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
538 | "*** NOT making bad root inode %pU\n", | ||
539 | get_khandle_from_ino(inode)); | ||
540 | } else { | ||
541 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
542 | "*** making bad inode %pU\n", | ||
543 | get_khandle_from_ino(inode)); | ||
544 | make_bad_inode(inode); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | /* | ||
549 | * The following is a very dirty hack that is now a permanent part of the | ||
550 | * ORANGEFS protocol. See protocol.h for more error definitions. | ||
551 | */ | ||
552 | |||
553 | /* The order matches include/orangefs-types.h in the OrangeFS source. */ | ||
554 | static int PINT_errno_mapping[] = { | ||
555 | 0, EPERM, ENOENT, EINTR, EIO, ENXIO, EBADF, EAGAIN, ENOMEM, | ||
556 | EFAULT, EBUSY, EEXIST, ENODEV, ENOTDIR, EISDIR, EINVAL, EMFILE, | ||
557 | EFBIG, ENOSPC, EROFS, EMLINK, EPIPE, EDEADLK, ENAMETOOLONG, | ||
558 | ENOLCK, ENOSYS, ENOTEMPTY, ELOOP, EWOULDBLOCK, ENOMSG, EUNATCH, | ||
559 | EBADR, EDEADLOCK, ENODATA, ETIME, ENONET, EREMOTE, ECOMM, | ||
560 | EPROTO, EBADMSG, EOVERFLOW, ERESTART, EMSGSIZE, EPROTOTYPE, | ||
561 | ENOPROTOOPT, EPROTONOSUPPORT, EOPNOTSUPP, EADDRINUSE, | ||
562 | EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, ENETRESET, ENOBUFS, | ||
563 | ETIMEDOUT, ECONNREFUSED, EHOSTDOWN, EHOSTUNREACH, EALREADY, | ||
564 | EACCES, ECONNRESET, ERANGE | ||
565 | }; | ||
566 | |||
567 | int orangefs_normalize_to_errno(__s32 error_code) | ||
568 | { | ||
569 | __u32 i; | ||
570 | |||
571 | /* Success */ | ||
572 | if (error_code == 0) { | ||
573 | return 0; | ||
574 | /* | ||
575 | * This shouldn't ever happen. If it does it should be fixed on the | ||
576 | * server. | ||
577 | */ | ||
578 | } else if (error_code > 0) { | ||
579 | gossip_err("orangefs: error status receieved.\n"); | ||
580 | gossip_err("orangefs: assuming error code is inverted.\n"); | ||
581 | error_code = -error_code; | ||
582 | } | ||
583 | |||
584 | /* | ||
585 | * XXX: This is very bad since error codes from ORANGEFS may not be | ||
586 | * suitable for return into userspace. | ||
587 | */ | ||
588 | |||
589 | /* | ||
590 | * Convert ORANGEFS error values into errno values suitable for return | ||
591 | * from the kernel. | ||
592 | */ | ||
593 | if ((-error_code) & ORANGEFS_NON_ERRNO_ERROR_BIT) { | ||
594 | if (((-error_code) & | ||
595 | (ORANGEFS_ERROR_NUMBER_BITS|ORANGEFS_NON_ERRNO_ERROR_BIT| | ||
596 | ORANGEFS_ERROR_BIT)) == ORANGEFS_ECANCEL) { | ||
597 | /* | ||
598 | * cancellation error codes generally correspond to | ||
599 | * a timeout from the client's perspective | ||
600 | */ | ||
601 | error_code = -ETIMEDOUT; | ||
602 | } else { | ||
603 | /* assume a default error code */ | ||
604 | gossip_err("orangefs: warning: got error code without errno equivalent: %d.\n", error_code); | ||
605 | error_code = -EINVAL; | ||
606 | } | ||
607 | |||
608 | /* Convert ORANGEFS encoded errno values into regular errno values. */ | ||
609 | } else if ((-error_code) & ORANGEFS_ERROR_BIT) { | ||
610 | i = (-error_code) & ~(ORANGEFS_ERROR_BIT|ORANGEFS_ERROR_CLASS_BITS); | ||
611 | if (i < sizeof(PINT_errno_mapping)/sizeof(*PINT_errno_mapping)) | ||
612 | error_code = -PINT_errno_mapping[i]; | ||
613 | else | ||
614 | error_code = -EINVAL; | ||
615 | |||
616 | /* | ||
617 | * Only ORANGEFS protocol error codes should ever come here. Otherwise | ||
618 | * there is a bug somewhere. | ||
619 | */ | ||
620 | } else { | ||
621 | gossip_err("orangefs: orangefs_normalize_to_errno: got error code which is not from ORANGEFS.\n"); | ||
622 | } | ||
623 | return error_code; | ||
624 | } | ||
625 | |||
626 | #define NUM_MODES 11 | ||
627 | __s32 ORANGEFS_util_translate_mode(int mode) | ||
628 | { | ||
629 | int ret = 0; | ||
630 | int i = 0; | ||
631 | static int modes[NUM_MODES] = { | ||
632 | S_IXOTH, S_IWOTH, S_IROTH, | ||
633 | S_IXGRP, S_IWGRP, S_IRGRP, | ||
634 | S_IXUSR, S_IWUSR, S_IRUSR, | ||
635 | S_ISGID, S_ISUID | ||
636 | }; | ||
637 | static int orangefs_modes[NUM_MODES] = { | ||
638 | ORANGEFS_O_EXECUTE, ORANGEFS_O_WRITE, ORANGEFS_O_READ, | ||
639 | ORANGEFS_G_EXECUTE, ORANGEFS_G_WRITE, ORANGEFS_G_READ, | ||
640 | ORANGEFS_U_EXECUTE, ORANGEFS_U_WRITE, ORANGEFS_U_READ, | ||
641 | ORANGEFS_G_SGID, ORANGEFS_U_SUID | ||
642 | }; | ||
643 | |||
644 | for (i = 0; i < NUM_MODES; i++) | ||
645 | if (mode & modes[i]) | ||
646 | ret |= orangefs_modes[i]; | ||
647 | |||
648 | return ret; | ||
649 | } | ||
650 | #undef NUM_MODES | ||
651 | |||
652 | /* | ||
653 | * After obtaining a string representation of the client's debug | ||
654 | * keywords and their associated masks, this function is called to build an | ||
655 | * array of these values. | ||
656 | */ | ||
657 | int orangefs_prepare_cdm_array(char *debug_array_string) | ||
658 | { | ||
659 | int i; | ||
660 | int rc = -EINVAL; | ||
661 | char *cds_head = NULL; | ||
662 | char *cds_delimiter = NULL; | ||
663 | int keyword_len = 0; | ||
664 | |||
665 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); | ||
666 | |||
667 | /* | ||
668 | * figure out how many elements the cdm_array needs. | ||
669 | */ | ||
670 | for (i = 0; i < strlen(debug_array_string); i++) | ||
671 | if (debug_array_string[i] == '\n') | ||
672 | cdm_element_count++; | ||
673 | |||
674 | if (!cdm_element_count) { | ||
675 | pr_info("No elements in client debug array string!\n"); | ||
676 | goto out; | ||
677 | } | ||
678 | |||
679 | cdm_array = | ||
680 | kzalloc(cdm_element_count * sizeof(struct client_debug_mask), | ||
681 | GFP_KERNEL); | ||
682 | if (!cdm_array) { | ||
683 | pr_info("malloc failed for cdm_array!\n"); | ||
684 | rc = -ENOMEM; | ||
685 | goto out; | ||
686 | } | ||
687 | |||
688 | cds_head = debug_array_string; | ||
689 | |||
690 | for (i = 0; i < cdm_element_count; i++) { | ||
691 | cds_delimiter = strchr(cds_head, '\n'); | ||
692 | *cds_delimiter = '\0'; | ||
693 | |||
694 | keyword_len = strcspn(cds_head, " "); | ||
695 | |||
696 | cdm_array[i].keyword = kzalloc(keyword_len + 1, GFP_KERNEL); | ||
697 | if (!cdm_array[i].keyword) { | ||
698 | rc = -ENOMEM; | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | sscanf(cds_head, | ||
703 | "%s %llx %llx", | ||
704 | cdm_array[i].keyword, | ||
705 | (unsigned long long *)&(cdm_array[i].mask1), | ||
706 | (unsigned long long *)&(cdm_array[i].mask2)); | ||
707 | |||
708 | if (!strcmp(cdm_array[i].keyword, ORANGEFS_VERBOSE)) | ||
709 | client_verbose_index = i; | ||
710 | |||
711 | if (!strcmp(cdm_array[i].keyword, ORANGEFS_ALL)) | ||
712 | client_all_index = i; | ||
713 | |||
714 | cds_head = cds_delimiter + 1; | ||
715 | } | ||
716 | |||
717 | rc = cdm_element_count; | ||
718 | |||
719 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: rc:%d:\n", __func__, rc); | ||
720 | |||
721 | out: | ||
722 | |||
723 | return rc; | ||
724 | |||
725 | } | ||
726 | |||
727 | /* | ||
728 | * /sys/kernel/debug/orangefs/debug-help can be catted to | ||
729 | * see all the available kernel and client debug keywords. | ||
730 | * | ||
731 | * When the kernel boots, we have no idea what keywords the | ||
732 | * client supports, nor their associated masks. | ||
733 | * | ||
734 | * We pass through this function once at boot and stamp a | ||
735 | * boilerplate "we don't know" message for the client in the | ||
736 | * debug-help file. We pass through here again when the client | ||
737 | * starts and then we can fill out the debug-help file fully. | ||
738 | * | ||
739 | * The client might be restarted any number of times between | ||
740 | * reboots, we only build the debug-help file the first time. | ||
741 | */ | ||
742 | int orangefs_prepare_debugfs_help_string(int at_boot) | ||
743 | { | ||
744 | int rc = -EINVAL; | ||
745 | int i; | ||
746 | int byte_count = 0; | ||
747 | char *client_title = "Client Debug Keywords:\n"; | ||
748 | char *kernel_title = "Kernel Debug Keywords:\n"; | ||
749 | |||
750 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); | ||
751 | |||
752 | if (at_boot) { | ||
753 | byte_count += strlen(HELP_STRING_UNINITIALIZED); | ||
754 | client_title = HELP_STRING_UNINITIALIZED; | ||
755 | } else { | ||
756 | /* | ||
757 | * fill the client keyword/mask array and remember | ||
758 | * how many elements there were. | ||
759 | */ | ||
760 | cdm_element_count = | ||
761 | orangefs_prepare_cdm_array(client_debug_array_string); | ||
762 | if (cdm_element_count <= 0) | ||
763 | goto out; | ||
764 | |||
765 | /* Count the bytes destined for debug_help_string. */ | ||
766 | byte_count += strlen(client_title); | ||
767 | |||
768 | for (i = 0; i < cdm_element_count; i++) { | ||
769 | byte_count += strlen(cdm_array[i].keyword + 2); | ||
770 | if (byte_count >= DEBUG_HELP_STRING_SIZE) { | ||
771 | pr_info("%s: overflow 1!\n", __func__); | ||
772 | goto out; | ||
773 | } | ||
774 | } | ||
775 | |||
776 | gossip_debug(GOSSIP_UTILS_DEBUG, | ||
777 | "%s: cdm_element_count:%d:\n", | ||
778 | __func__, | ||
779 | cdm_element_count); | ||
780 | } | ||
781 | |||
782 | byte_count += strlen(kernel_title); | ||
783 | for (i = 0; i < num_kmod_keyword_mask_map; i++) { | ||
784 | byte_count += | ||
785 | strlen(s_kmod_keyword_mask_map[i].keyword + 2); | ||
786 | if (byte_count >= DEBUG_HELP_STRING_SIZE) { | ||
787 | pr_info("%s: overflow 2!\n", __func__); | ||
788 | goto out; | ||
789 | } | ||
790 | } | ||
791 | |||
792 | /* build debug_help_string. */ | ||
793 | debug_help_string = kzalloc(DEBUG_HELP_STRING_SIZE, GFP_KERNEL); | ||
794 | if (!debug_help_string) { | ||
795 | rc = -ENOMEM; | ||
796 | goto out; | ||
797 | } | ||
798 | |||
799 | strcat(debug_help_string, client_title); | ||
800 | |||
801 | if (!at_boot) { | ||
802 | for (i = 0; i < cdm_element_count; i++) { | ||
803 | strcat(debug_help_string, "\t"); | ||
804 | strcat(debug_help_string, cdm_array[i].keyword); | ||
805 | strcat(debug_help_string, "\n"); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | strcat(debug_help_string, "\n"); | ||
810 | strcat(debug_help_string, kernel_title); | ||
811 | |||
812 | for (i = 0; i < num_kmod_keyword_mask_map; i++) { | ||
813 | strcat(debug_help_string, "\t"); | ||
814 | strcat(debug_help_string, s_kmod_keyword_mask_map[i].keyword); | ||
815 | strcat(debug_help_string, "\n"); | ||
816 | } | ||
817 | |||
818 | rc = 0; | ||
819 | |||
820 | out: | ||
821 | |||
822 | return rc; | ||
823 | |||
824 | } | ||
825 | |||
826 | /* | ||
827 | * kernel = type 0 | ||
828 | * client = type 1 | ||
829 | */ | ||
830 | void debug_mask_to_string(void *mask, int type) | ||
831 | { | ||
832 | int i; | ||
833 | int len = 0; | ||
834 | char *debug_string; | ||
835 | int element_count = 0; | ||
836 | |||
837 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); | ||
838 | |||
839 | if (type) { | ||
840 | debug_string = client_debug_string; | ||
841 | element_count = cdm_element_count; | ||
842 | } else { | ||
843 | debug_string = kernel_debug_string; | ||
844 | element_count = num_kmod_keyword_mask_map; | ||
845 | } | ||
846 | |||
847 | memset(debug_string, 0, ORANGEFS_MAX_DEBUG_STRING_LEN); | ||
848 | |||
849 | /* | ||
850 | * Some keywords, like "all" or "verbose", are amalgams of | ||
851 | * numerous other keywords. Make a special check for those | ||
852 | * before grinding through the whole mask only to find out | ||
853 | * later... | ||
854 | */ | ||
855 | if (check_amalgam_keyword(mask, type)) | ||
856 | goto out; | ||
857 | |||
858 | /* Build the debug string. */ | ||
859 | for (i = 0; i < element_count; i++) | ||
860 | if (type) | ||
861 | do_c_string(mask, i); | ||
862 | else | ||
863 | do_k_string(mask, i); | ||
864 | |||
865 | len = strlen(debug_string); | ||
866 | |||
867 | if ((len) && (type)) | ||
868 | client_debug_string[len - 1] = '\0'; | ||
869 | else if (len) | ||
870 | kernel_debug_string[len - 1] = '\0'; | ||
871 | else if (type) | ||
872 | strcpy(client_debug_string, "none"); | ||
873 | else | ||
874 | strcpy(kernel_debug_string, "none"); | ||
875 | |||
876 | out: | ||
877 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: string:%s:\n", __func__, debug_string); | ||
878 | |||
879 | return; | ||
880 | |||
881 | } | ||
882 | |||
883 | void do_k_string(void *k_mask, int index) | ||
884 | { | ||
885 | __u64 *mask = (__u64 *) k_mask; | ||
886 | |||
887 | if (keyword_is_amalgam((char *) s_kmod_keyword_mask_map[index].keyword)) | ||
888 | goto out; | ||
889 | |||
890 | if (*mask & s_kmod_keyword_mask_map[index].mask_val) { | ||
891 | if ((strlen(kernel_debug_string) + | ||
892 | strlen(s_kmod_keyword_mask_map[index].keyword)) | ||
893 | < ORANGEFS_MAX_DEBUG_STRING_LEN - 1) { | ||
894 | strcat(kernel_debug_string, | ||
895 | s_kmod_keyword_mask_map[index].keyword); | ||
896 | strcat(kernel_debug_string, ","); | ||
897 | } else { | ||
898 | gossip_err("%s: overflow!\n", __func__); | ||
899 | strcpy(kernel_debug_string, ORANGEFS_ALL); | ||
900 | goto out; | ||
901 | } | ||
902 | } | ||
903 | |||
904 | out: | ||
905 | |||
906 | return; | ||
907 | } | ||
908 | |||
909 | void do_c_string(void *c_mask, int index) | ||
910 | { | ||
911 | struct client_debug_mask *mask = (struct client_debug_mask *) c_mask; | ||
912 | |||
913 | if (keyword_is_amalgam(cdm_array[index].keyword)) | ||
914 | goto out; | ||
915 | |||
916 | if ((mask->mask1 & cdm_array[index].mask1) || | ||
917 | (mask->mask2 & cdm_array[index].mask2)) { | ||
918 | if ((strlen(client_debug_string) + | ||
919 | strlen(cdm_array[index].keyword) + 1) | ||
920 | < ORANGEFS_MAX_DEBUG_STRING_LEN - 2) { | ||
921 | strcat(client_debug_string, | ||
922 | cdm_array[index].keyword); | ||
923 | strcat(client_debug_string, ","); | ||
924 | } else { | ||
925 | gossip_err("%s: overflow!\n", __func__); | ||
926 | strcpy(client_debug_string, ORANGEFS_ALL); | ||
927 | goto out; | ||
928 | } | ||
929 | } | ||
930 | out: | ||
931 | return; | ||
932 | } | ||
933 | |||
934 | int keyword_is_amalgam(char *keyword) | ||
935 | { | ||
936 | int rc = 0; | ||
937 | |||
938 | if ((!strcmp(keyword, ORANGEFS_ALL)) || (!strcmp(keyword, ORANGEFS_VERBOSE))) | ||
939 | rc = 1; | ||
940 | |||
941 | return rc; | ||
942 | } | ||
943 | |||
944 | /* | ||
945 | * kernel = type 0 | ||
946 | * client = type 1 | ||
947 | * | ||
948 | * return 1 if we found an amalgam. | ||
949 | */ | ||
950 | int check_amalgam_keyword(void *mask, int type) | ||
951 | { | ||
952 | __u64 *k_mask; | ||
953 | struct client_debug_mask *c_mask; | ||
954 | int k_all_index = num_kmod_keyword_mask_map - 1; | ||
955 | int rc = 0; | ||
956 | |||
957 | if (type) { | ||
958 | c_mask = (struct client_debug_mask *) mask; | ||
959 | |||
960 | if ((c_mask->mask1 == cdm_array[client_all_index].mask1) && | ||
961 | (c_mask->mask2 == cdm_array[client_all_index].mask2)) { | ||
962 | strcpy(client_debug_string, ORANGEFS_ALL); | ||
963 | rc = 1; | ||
964 | goto out; | ||
965 | } | ||
966 | |||
967 | if ((c_mask->mask1 == cdm_array[client_verbose_index].mask1) && | ||
968 | (c_mask->mask2 == cdm_array[client_verbose_index].mask2)) { | ||
969 | strcpy(client_debug_string, ORANGEFS_VERBOSE); | ||
970 | rc = 1; | ||
971 | goto out; | ||
972 | } | ||
973 | |||
974 | } else { | ||
975 | k_mask = (__u64 *) mask; | ||
976 | |||
977 | if (*k_mask >= s_kmod_keyword_mask_map[k_all_index].mask_val) { | ||
978 | strcpy(kernel_debug_string, ORANGEFS_ALL); | ||
979 | rc = 1; | ||
980 | goto out; | ||
981 | } | ||
982 | } | ||
983 | |||
984 | out: | ||
985 | |||
986 | return rc; | ||
987 | } | ||
988 | |||
989 | /* | ||
990 | * kernel = type 0 | ||
991 | * client = type 1 | ||
992 | */ | ||
993 | void debug_string_to_mask(char *debug_string, void *mask, int type) | ||
994 | { | ||
995 | char *unchecked_keyword; | ||
996 | int i; | ||
997 | char *strsep_fodder = kstrdup(debug_string, GFP_KERNEL); | ||
998 | char *original_pointer; | ||
999 | int element_count = 0; | ||
1000 | struct client_debug_mask *c_mask; | ||
1001 | __u64 *k_mask; | ||
1002 | |||
1003 | gossip_debug(GOSSIP_UTILS_DEBUG, "%s: start\n", __func__); | ||
1004 | |||
1005 | if (type) { | ||
1006 | c_mask = (struct client_debug_mask *)mask; | ||
1007 | element_count = cdm_element_count; | ||
1008 | } else { | ||
1009 | k_mask = (__u64 *)mask; | ||
1010 | *k_mask = 0; | ||
1011 | element_count = num_kmod_keyword_mask_map; | ||
1012 | } | ||
1013 | |||
1014 | original_pointer = strsep_fodder; | ||
1015 | while ((unchecked_keyword = strsep(&strsep_fodder, ","))) | ||
1016 | if (strlen(unchecked_keyword)) { | ||
1017 | for (i = 0; i < element_count; i++) | ||
1018 | if (type) | ||
1019 | do_c_mask(i, | ||
1020 | unchecked_keyword, | ||
1021 | &c_mask); | ||
1022 | else | ||
1023 | do_k_mask(i, | ||
1024 | unchecked_keyword, | ||
1025 | &k_mask); | ||
1026 | } | ||
1027 | |||
1028 | kfree(original_pointer); | ||
1029 | } | ||
1030 | |||
1031 | void do_c_mask(int i, | ||
1032 | char *unchecked_keyword, | ||
1033 | struct client_debug_mask **sane_mask) | ||
1034 | { | ||
1035 | |||
1036 | if (!strcmp(cdm_array[i].keyword, unchecked_keyword)) { | ||
1037 | (**sane_mask).mask1 = (**sane_mask).mask1 | cdm_array[i].mask1; | ||
1038 | (**sane_mask).mask2 = (**sane_mask).mask2 | cdm_array[i].mask2; | ||
1039 | } | ||
1040 | } | ||
1041 | |||
1042 | void do_k_mask(int i, char *unchecked_keyword, __u64 **sane_mask) | ||
1043 | { | ||
1044 | |||
1045 | if (!strcmp(s_kmod_keyword_mask_map[i].keyword, unchecked_keyword)) | ||
1046 | **sane_mask = (**sane_mask) | | ||
1047 | s_kmod_keyword_mask_map[i].mask_val; | ||
1048 | } | ||