Commit f48b7399840b453e7282b523f535561fe9638a2d
1 parent
0dc1ba24f7
Exists in
master
and in
20 other branches
LSM: split LSM_AUDIT_DATA_FS into _PATH and _INODE
The lsm common audit code has wacky contortions making sure which pieces of information are set based on if it was given a path, dentry, or inode. Split this into path and inode to get rid of some of the code complexity. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Showing 6 changed files with 78 additions and 73 deletions Side-by-side Diff
include/linux/lsm_audit.h
... | ... | @@ -27,7 +27,7 @@ |
27 | 27 | /* Auxiliary data to use in generating the audit record. */ |
28 | 28 | struct common_audit_data { |
29 | 29 | char type; |
30 | -#define LSM_AUDIT_DATA_FS 1 | |
30 | +#define LSM_AUDIT_DATA_PATH 1 | |
31 | 31 | #define LSM_AUDIT_DATA_NET 2 |
32 | 32 | #define LSM_AUDIT_DATA_CAP 3 |
33 | 33 | #define LSM_AUDIT_DATA_IPC 4 |
34 | 34 | |
... | ... | @@ -35,12 +35,11 @@ |
35 | 35 | #define LSM_AUDIT_DATA_KEY 6 |
36 | 36 | #define LSM_AUDIT_DATA_NONE 7 |
37 | 37 | #define LSM_AUDIT_DATA_KMOD 8 |
38 | +#define LSM_AUDIT_DATA_INODE 9 | |
38 | 39 | struct task_struct *tsk; |
39 | 40 | union { |
40 | - struct { | |
41 | - struct path path; | |
42 | - struct inode *inode; | |
43 | - } fs; | |
41 | + struct path path; | |
42 | + struct inode *inode; | |
44 | 43 | struct { |
45 | 44 | int netif; |
46 | 45 | struct sock *sk; |
security/lsm_audit.c
... | ... | @@ -210,7 +210,6 @@ |
210 | 210 | static void dump_common_audit_data(struct audit_buffer *ab, |
211 | 211 | struct common_audit_data *a) |
212 | 212 | { |
213 | - struct inode *inode = NULL; | |
214 | 213 | struct task_struct *tsk = current; |
215 | 214 | |
216 | 215 | if (a->tsk) |
217 | 216 | |
218 | 217 | |
... | ... | @@ -229,33 +228,40 @@ |
229 | 228 | case LSM_AUDIT_DATA_CAP: |
230 | 229 | audit_log_format(ab, " capability=%d ", a->u.cap); |
231 | 230 | break; |
232 | - case LSM_AUDIT_DATA_FS: | |
233 | - if (a->u.fs.path.dentry) { | |
234 | - struct dentry *dentry = a->u.fs.path.dentry; | |
235 | - if (a->u.fs.path.mnt) { | |
236 | - audit_log_d_path(ab, "path=", &a->u.fs.path); | |
237 | - } else { | |
238 | - audit_log_format(ab, " name="); | |
239 | - audit_log_untrustedstring(ab, | |
240 | - dentry->d_name.name); | |
241 | - } | |
242 | - inode = dentry->d_inode; | |
243 | - } else if (a->u.fs.inode) { | |
244 | - struct dentry *dentry; | |
245 | - inode = a->u.fs.inode; | |
246 | - dentry = d_find_alias(inode); | |
247 | - if (dentry) { | |
248 | - audit_log_format(ab, " name="); | |
249 | - audit_log_untrustedstring(ab, | |
250 | - dentry->d_name.name); | |
251 | - dput(dentry); | |
252 | - } | |
231 | + case LSM_AUDIT_DATA_PATH: { | |
232 | + struct dentry *dentry = a->u.path.dentry; | |
233 | + struct inode *inode; | |
234 | + | |
235 | + if (a->u.path.mnt) { | |
236 | + audit_log_d_path(ab, "path=", &a->u.path); | |
237 | + } else { | |
238 | + audit_log_format(ab, " name="); | |
239 | + audit_log_untrustedstring(ab, | |
240 | + dentry->d_name.name); | |
253 | 241 | } |
242 | + inode = dentry->d_inode; | |
254 | 243 | if (inode) |
255 | 244 | audit_log_format(ab, " dev=%s ino=%lu", |
256 | 245 | inode->i_sb->s_id, |
257 | 246 | inode->i_ino); |
258 | 247 | break; |
248 | + } | |
249 | + case LSM_AUDIT_DATA_INODE: { | |
250 | + struct dentry *dentry; | |
251 | + struct inode *inode; | |
252 | + | |
253 | + inode = a->u.inode; | |
254 | + dentry = d_find_alias(inode); | |
255 | + if (dentry) { | |
256 | + audit_log_format(ab, " name="); | |
257 | + audit_log_untrustedstring(ab, | |
258 | + dentry->d_name.name); | |
259 | + dput(dentry); | |
260 | + } | |
261 | + audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id, | |
262 | + inode->i_ino); | |
263 | + break; | |
264 | + } | |
259 | 265 | case LSM_AUDIT_DATA_TASK: |
260 | 266 | tsk = a->u.tsk; |
261 | 267 | if (tsk && tsk->pid) { |
security/selinux/avc.c
... | ... | @@ -531,7 +531,7 @@ |
531 | 531 | * during retry. However this is logically just as if the operation |
532 | 532 | * happened a little later. |
533 | 533 | */ |
534 | - if ((a->type == LSM_AUDIT_DATA_FS) && | |
534 | + if ((a->type == LSM_AUDIT_DATA_INODE) && | |
535 | 535 | (flags & IPERM_FLAG_RCU)) |
536 | 536 | return -ECHILD; |
537 | 537 |
security/selinux/hooks.c
... | ... | @@ -1488,8 +1488,8 @@ |
1488 | 1488 | |
1489 | 1489 | if (!adp) { |
1490 | 1490 | adp = &ad; |
1491 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1492 | - ad.u.fs.inode = inode; | |
1491 | + COMMON_AUDIT_DATA_INIT(&ad, INODE); | |
1492 | + ad.u.inode = inode; | |
1493 | 1493 | } |
1494 | 1494 | |
1495 | 1495 | return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); |
... | ... | @@ -1506,9 +1506,9 @@ |
1506 | 1506 | struct inode *inode = dentry->d_inode; |
1507 | 1507 | struct common_audit_data ad; |
1508 | 1508 | |
1509 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1510 | - ad.u.fs.path.mnt = mnt; | |
1511 | - ad.u.fs.path.dentry = dentry; | |
1509 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1510 | + ad.u.path.mnt = mnt; | |
1511 | + ad.u.path.dentry = dentry; | |
1512 | 1512 | return inode_has_perm(cred, inode, av, &ad, 0); |
1513 | 1513 | } |
1514 | 1514 | |
... | ... | @@ -1530,8 +1530,8 @@ |
1530 | 1530 | u32 sid = cred_sid(cred); |
1531 | 1531 | int rc; |
1532 | 1532 | |
1533 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1534 | - ad.u.fs.path = file->f_path; | |
1533 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1534 | + ad.u.path = file->f_path; | |
1535 | 1535 | |
1536 | 1536 | if (sid != fsec->sid) { |
1537 | 1537 | rc = avc_has_perm(sid, fsec->sid, |
... | ... | @@ -1569,8 +1569,8 @@ |
1569 | 1569 | sid = tsec->sid; |
1570 | 1570 | newsid = tsec->create_sid; |
1571 | 1571 | |
1572 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1573 | - ad.u.fs.path.dentry = dentry; | |
1572 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1573 | + ad.u.path.dentry = dentry; | |
1574 | 1574 | |
1575 | 1575 | rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, |
1576 | 1576 | DIR__ADD_NAME | DIR__SEARCH, |
... | ... | @@ -1621,8 +1621,8 @@ |
1621 | 1621 | dsec = dir->i_security; |
1622 | 1622 | isec = dentry->d_inode->i_security; |
1623 | 1623 | |
1624 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1625 | - ad.u.fs.path.dentry = dentry; | |
1624 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1625 | + ad.u.path.dentry = dentry; | |
1626 | 1626 | |
1627 | 1627 | av = DIR__SEARCH; |
1628 | 1628 | av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); |
1629 | 1629 | |
... | ... | @@ -1667,9 +1667,9 @@ |
1667 | 1667 | old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); |
1668 | 1668 | new_dsec = new_dir->i_security; |
1669 | 1669 | |
1670 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1670 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1671 | 1671 | |
1672 | - ad.u.fs.path.dentry = old_dentry; | |
1672 | + ad.u.path.dentry = old_dentry; | |
1673 | 1673 | rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, |
1674 | 1674 | DIR__REMOVE_NAME | DIR__SEARCH, &ad); |
1675 | 1675 | if (rc) |
... | ... | @@ -1685,7 +1685,7 @@ |
1685 | 1685 | return rc; |
1686 | 1686 | } |
1687 | 1687 | |
1688 | - ad.u.fs.path.dentry = new_dentry; | |
1688 | + ad.u.path.dentry = new_dentry; | |
1689 | 1689 | av = DIR__ADD_NAME | DIR__SEARCH; |
1690 | 1690 | if (new_dentry->d_inode) |
1691 | 1691 | av |= DIR__REMOVE_NAME; |
... | ... | @@ -1991,8 +1991,8 @@ |
1991 | 1991 | return rc; |
1992 | 1992 | } |
1993 | 1993 | |
1994 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
1995 | - ad.u.fs.path = bprm->file->f_path; | |
1994 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
1995 | + ad.u.path = bprm->file->f_path; | |
1996 | 1996 | |
1997 | 1997 | if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) |
1998 | 1998 | new_tsec->sid = old_tsec->sid; |
... | ... | @@ -2120,7 +2120,7 @@ |
2120 | 2120 | |
2121 | 2121 | /* Revalidate access to inherited open files. */ |
2122 | 2122 | |
2123 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
2123 | + COMMON_AUDIT_DATA_INIT(&ad, INODE); | |
2124 | 2124 | |
2125 | 2125 | spin_lock(&files->file_lock); |
2126 | 2126 | for (;;) { |
... | ... | @@ -2468,8 +2468,8 @@ |
2468 | 2468 | if (flags & MS_KERNMOUNT) |
2469 | 2469 | return 0; |
2470 | 2470 | |
2471 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
2472 | - ad.u.fs.path.dentry = sb->s_root; | |
2471 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
2472 | + ad.u.path.dentry = sb->s_root; | |
2473 | 2473 | return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); |
2474 | 2474 | } |
2475 | 2475 | |
... | ... | @@ -2478,8 +2478,8 @@ |
2478 | 2478 | const struct cred *cred = current_cred(); |
2479 | 2479 | struct common_audit_data ad; |
2480 | 2480 | |
2481 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
2482 | - ad.u.fs.path.dentry = dentry->d_sb->s_root; | |
2481 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
2482 | + ad.u.path.dentry = dentry->d_sb->s_root; | |
2483 | 2483 | return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); |
2484 | 2484 | } |
2485 | 2485 | |
... | ... | @@ -2653,8 +2653,8 @@ |
2653 | 2653 | if (!mask) |
2654 | 2654 | return 0; |
2655 | 2655 | |
2656 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
2657 | - ad.u.fs.inode = inode; | |
2656 | + COMMON_AUDIT_DATA_INIT(&ad, INODE); | |
2657 | + ad.u.inode = inode; | |
2658 | 2658 | |
2659 | 2659 | if (from_access) |
2660 | 2660 | ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; |
... | ... | @@ -2732,8 +2732,8 @@ |
2732 | 2732 | if (!is_owner_or_cap(inode)) |
2733 | 2733 | return -EPERM; |
2734 | 2734 | |
2735 | - COMMON_AUDIT_DATA_INIT(&ad, FS); | |
2736 | - ad.u.fs.path.dentry = dentry; | |
2735 | + COMMON_AUDIT_DATA_INIT(&ad, PATH); | |
2736 | + ad.u.path.dentry = dentry; | |
2737 | 2737 | |
2738 | 2738 | rc = avc_has_perm(sid, isec->sid, isec->sclass, |
2739 | 2739 | FILE__RELABELFROM, &ad); |
security/smack/smack.h
... | ... | @@ -316,22 +316,22 @@ |
316 | 316 | static inline void smk_ad_setfield_u_fs_path_dentry(struct smk_audit_info *a, |
317 | 317 | struct dentry *d) |
318 | 318 | { |
319 | - a->a.u.fs.path.dentry = d; | |
319 | + a->a.u.path.dentry = d; | |
320 | 320 | } |
321 | 321 | static inline void smk_ad_setfield_u_fs_path_mnt(struct smk_audit_info *a, |
322 | 322 | struct vfsmount *m) |
323 | 323 | { |
324 | - a->a.u.fs.path.mnt = m; | |
324 | + a->a.u.path.mnt = m; | |
325 | 325 | } |
326 | 326 | static inline void smk_ad_setfield_u_fs_inode(struct smk_audit_info *a, |
327 | 327 | struct inode *i) |
328 | 328 | { |
329 | - a->a.u.fs.inode = i; | |
329 | + a->a.u.inode = i; | |
330 | 330 | } |
331 | 331 | static inline void smk_ad_setfield_u_fs_path(struct smk_audit_info *a, |
332 | 332 | struct path p) |
333 | 333 | { |
334 | - a->a.u.fs.path = p; | |
334 | + a->a.u.path = p; | |
335 | 335 | } |
336 | 336 | static inline void smk_ad_setfield_u_net_sk(struct smk_audit_info *a, |
337 | 337 | struct sock *sk) |
security/smack/smack_lsm.c
... | ... | @@ -383,7 +383,7 @@ |
383 | 383 | int rc; |
384 | 384 | struct smk_audit_info ad; |
385 | 385 | |
386 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
386 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
387 | 387 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
388 | 388 | |
389 | 389 | rc = smk_curacc(sbp->smk_floor, MAY_READ, &ad); |
... | ... | @@ -407,7 +407,7 @@ |
407 | 407 | struct superblock_smack *sbp = path->mnt->mnt_sb->s_security; |
408 | 408 | struct smk_audit_info ad; |
409 | 409 | |
410 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
410 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
411 | 411 | smk_ad_setfield_u_fs_path(&ad, *path); |
412 | 412 | |
413 | 413 | return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); |
... | ... | @@ -426,7 +426,7 @@ |
426 | 426 | struct superblock_smack *sbp; |
427 | 427 | struct smk_audit_info ad; |
428 | 428 | |
429 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
429 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
430 | 430 | smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_root); |
431 | 431 | smk_ad_setfield_u_fs_path_mnt(&ad, mnt); |
432 | 432 | |
... | ... | @@ -563,7 +563,7 @@ |
563 | 563 | struct smk_audit_info ad; |
564 | 564 | int rc; |
565 | 565 | |
566 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
566 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
567 | 567 | smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); |
568 | 568 | |
569 | 569 | isp = smk_of_inode(old_dentry->d_inode); |
... | ... | @@ -592,7 +592,7 @@ |
592 | 592 | struct smk_audit_info ad; |
593 | 593 | int rc; |
594 | 594 | |
595 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
595 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
596 | 596 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
597 | 597 | |
598 | 598 | /* |
... | ... | @@ -623,7 +623,7 @@ |
623 | 623 | struct smk_audit_info ad; |
624 | 624 | int rc; |
625 | 625 | |
626 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
626 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
627 | 627 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
628 | 628 | |
629 | 629 | /* |
... | ... | @@ -663,7 +663,7 @@ |
663 | 663 | char *isp; |
664 | 664 | struct smk_audit_info ad; |
665 | 665 | |
666 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
666 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
667 | 667 | smk_ad_setfield_u_fs_path_dentry(&ad, old_dentry); |
668 | 668 | |
669 | 669 | isp = smk_of_inode(old_dentry->d_inode); |
... | ... | @@ -700,7 +700,7 @@ |
700 | 700 | /* May be droppable after audit */ |
701 | 701 | if (flags & IPERM_FLAG_RCU) |
702 | 702 | return -ECHILD; |
703 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
703 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_INODE); | |
704 | 704 | smk_ad_setfield_u_fs_inode(&ad, inode); |
705 | 705 | return smk_curacc(smk_of_inode(inode), mask, &ad); |
706 | 706 | } |
... | ... | @@ -720,7 +720,7 @@ |
720 | 720 | */ |
721 | 721 | if (iattr->ia_valid & ATTR_FORCE) |
722 | 722 | return 0; |
723 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
723 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
724 | 724 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
725 | 725 | |
726 | 726 | return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); |
... | ... | @@ -737,7 +737,7 @@ |
737 | 737 | { |
738 | 738 | struct smk_audit_info ad; |
739 | 739 | |
740 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
740 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
741 | 741 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
742 | 742 | smk_ad_setfield_u_fs_path_mnt(&ad, mnt); |
743 | 743 | return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); |
... | ... | @@ -784,7 +784,7 @@ |
784 | 784 | } else |
785 | 785 | rc = cap_inode_setxattr(dentry, name, value, size, flags); |
786 | 786 | |
787 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
787 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
788 | 788 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
789 | 789 | |
790 | 790 | if (rc == 0) |
... | ... | @@ -845,7 +845,7 @@ |
845 | 845 | { |
846 | 846 | struct smk_audit_info ad; |
847 | 847 | |
848 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
848 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
849 | 849 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
850 | 850 | |
851 | 851 | return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ, &ad); |
... | ... | @@ -877,7 +877,7 @@ |
877 | 877 | } else |
878 | 878 | rc = cap_inode_removexattr(dentry, name); |
879 | 879 | |
880 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
880 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
881 | 881 | smk_ad_setfield_u_fs_path_dentry(&ad, dentry); |
882 | 882 | if (rc == 0) |
883 | 883 | rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE, &ad); |
... | ... | @@ -1047,7 +1047,7 @@ |
1047 | 1047 | int rc = 0; |
1048 | 1048 | struct smk_audit_info ad; |
1049 | 1049 | |
1050 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
1050 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
1051 | 1051 | smk_ad_setfield_u_fs_path(&ad, file->f_path); |
1052 | 1052 | |
1053 | 1053 | if (_IOC_DIR(cmd) & _IOC_WRITE) |
... | ... | @@ -1070,7 +1070,7 @@ |
1070 | 1070 | { |
1071 | 1071 | struct smk_audit_info ad; |
1072 | 1072 | |
1073 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
1073 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
1074 | 1074 | smk_ad_setfield_u_fs_path_dentry(&ad, file->f_path.dentry); |
1075 | 1075 | return smk_curacc(file->f_security, MAY_WRITE, &ad); |
1076 | 1076 | } |
... | ... | @@ -1089,7 +1089,7 @@ |
1089 | 1089 | struct smk_audit_info ad; |
1090 | 1090 | int rc; |
1091 | 1091 | |
1092 | - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS); | |
1092 | + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); | |
1093 | 1093 | smk_ad_setfield_u_fs_path(&ad, file->f_path); |
1094 | 1094 | |
1095 | 1095 | switch (cmd) { |