4308ff983c2a63c667d770ca5080d1adf09cc861
[ira/wip.git] / source4 / torture / smb2 / lock.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    SMB2 lock test suite
5
6    Copyright (C) Stefan Metzmacher 2006
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28
29
30 #define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \
31     (torture_setting_bool(_tctx, "invalid_lock_range_support", true))
32 #define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false))
33
34 #define CHECK_STATUS(status, correct) do { \
35         const char *_cmt = "(" __location__ ")"; \
36         torture_assert_ntstatus_equal_goto(torture,status,correct,ret,done,_cmt); \
37 } while (0)
38
39 #define CHECK_VALUE(v, correct) do { \
40         const char *_cmt = "(" __location__ ")"; \
41         torture_assert_int_equal_goto(torture,v,correct,ret,done,_cmt); \
42 } while (0)
43
44 static bool test_valid_request(struct torture_context *torture, struct smb2_tree *tree)
45 {
46         bool ret = true;
47         NTSTATUS status;
48         struct smb2_handle h;
49         uint8_t buf[200];
50         struct smb2_lock lck;
51         struct smb2_lock_element el[2];
52
53         ZERO_STRUCT(buf);
54
55         status = torture_smb2_testfile(tree, "lock1.txt", &h);
56         CHECK_STATUS(status, NT_STATUS_OK);
57
58         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
59         CHECK_STATUS(status, NT_STATUS_OK);
60
61         lck.in.locks            = el;
62
63         torture_comment(torture, "Test request with 0 locks.\n");
64
65         lck.in.lock_count       = 0x0000;
66         lck.in.reserved         = 0x00000000;
67         lck.in.file.handle      = h;
68         el[0].offset            = 0x0000000000000000;
69         el[0].length            = 0x0000000000000000;
70         el[0].reserved          = 0x0000000000000000;
71         el[0].flags             = 0x00000000;
72         status = smb2_lock(tree, &lck);
73         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
74
75         lck.in.lock_count       = 0x0000;
76         lck.in.reserved         = 0x00000000;
77         lck.in.file.handle      = h;
78         el[0].offset            = 0;
79         el[0].length            = 0;
80         el[0].reserved          = 0x00000000;
81         el[0].flags             = SMB2_LOCK_FLAG_SHARED;
82         status = smb2_lock(tree, &lck);
83         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
84
85         lck.in.lock_count       = 0x0001;
86         lck.in.reserved         = 0x00000000;
87         lck.in.file.handle      = h;
88         el[0].offset            = 0;
89         el[0].length            = 0;
90         el[0].reserved          = 0x00000000;
91         el[0].flags             = SMB2_LOCK_FLAG_NONE;
92         status = smb2_lock(tree, &lck);
93         if (TARGET_IS_W2K8(torture)) {
94                 CHECK_STATUS(status, NT_STATUS_OK);
95                 torture_warning(torture, "Target has bug validating lock flags "
96                                          "parameter.\n");
97         } else {
98                 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
99         }
100
101         torture_comment(torture, "Test >63-bit lock requests.\n");
102
103         lck.in.file.handle.data[0] +=1;
104         status = smb2_lock(tree, &lck);
105         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
106         lck.in.file.handle.data[0] -=1;
107
108         lck.in.lock_count       = 0x0001;
109         lck.in.reserved         = 0x123ab1;
110         lck.in.file.handle      = h;
111         el[0].offset            = UINT64_MAX;
112         el[0].length            = UINT64_MAX;
113         el[0].reserved          = 0x00000000;
114         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
115                                   SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
116         status = smb2_lock(tree, &lck);
117         if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(torture)) {
118                 CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE);
119         } else {
120                 CHECK_STATUS(status, NT_STATUS_OK);
121                 CHECK_VALUE(lck.out.reserved, 0);
122         }
123
124         lck.in.reserved         = 0x123ab2;
125         status = smb2_lock(tree, &lck);
126         if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(torture)) {
127                 CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE);
128         } else {
129                 CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
130         }
131
132         torture_comment(torture, "Test basic lock stacking.\n");
133
134         lck.in.lock_count       = 0x0001;
135         lck.in.reserved         = 0x12345678;
136         lck.in.file.handle      = h;
137         el[0].offset            = UINT32_MAX;
138         el[0].length            = UINT32_MAX;
139         el[0].reserved          = 0x87654321;
140         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
141                                   SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
142         status = smb2_lock(tree, &lck);
143         CHECK_STATUS(status, NT_STATUS_OK);
144         CHECK_VALUE(lck.out.reserved, 0);
145
146         el[0].flags             = SMB2_LOCK_FLAG_SHARED;
147         status = smb2_lock(tree, &lck);
148         CHECK_STATUS(status, NT_STATUS_OK);
149         CHECK_VALUE(lck.out.reserved, 0);
150
151         status = smb2_lock(tree, &lck);
152         CHECK_STATUS(status, NT_STATUS_OK);
153         CHECK_VALUE(lck.out.reserved, 0);
154
155         lck.in.lock_count       = 0x0001;
156         lck.in.reserved         = 0x87654321;
157         lck.in.file.handle      = h;
158         el[0].offset            = 0x00000000FFFFFFFF;
159         el[0].length            = 0x00000000FFFFFFFF;
160         el[0].reserved          = 0x1234567;
161         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
162         status = smb2_lock(tree, &lck);
163         CHECK_STATUS(status, NT_STATUS_OK);
164
165         lck.in.lock_count       = 0x0001;
166         lck.in.reserved         = 0x1234567;
167         lck.in.file.handle      = h;
168         el[0].offset            = 0x00000000FFFFFFFF;
169         el[0].length            = 0x00000000FFFFFFFF;
170         el[0].reserved          = 0x00000000;
171         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
172         status = smb2_lock(tree, &lck);
173         CHECK_STATUS(status, NT_STATUS_OK);
174
175         status = smb2_lock(tree, &lck);
176         CHECK_STATUS(status, NT_STATUS_OK);
177         status = smb2_lock(tree, &lck);
178         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
179
180         torture_comment(torture, "Test flags field permutations.\n");
181
182         lck.in.lock_count       = 0x0001;
183         lck.in.reserved         = 0;
184         lck.in.file.handle      = h;
185         el[0].offset            = 1;
186         el[0].length            = 1;
187         el[0].reserved          = 0x00000000;
188         el[0].flags             = ~SMB2_LOCK_FLAG_ALL_MASK;
189
190         status = smb2_lock(tree, &lck);
191         if (TARGET_IS_W2K8(torture)) {
192                 CHECK_STATUS(status, NT_STATUS_OK);
193                 torture_warning(torture, "Target has bug validating lock flags "
194                                          "parameter.\n");
195         } else {
196                 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
197         }
198
199         if (TARGET_IS_W2K8(torture)) {
200                 el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
201                 status = smb2_lock(tree, &lck);
202                 CHECK_STATUS(status, NT_STATUS_OK);
203         }
204
205         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
206         status = smb2_lock(tree, &lck);
207         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
208
209         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK |
210                                   SMB2_LOCK_FLAG_EXCLUSIVE;
211         status = smb2_lock(tree, &lck);
212         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
213
214         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK |
215                                   SMB2_LOCK_FLAG_SHARED;
216         status = smb2_lock(tree, &lck);
217         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
218
219         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK |
220                                   SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
221         status = smb2_lock(tree, &lck);
222         if (TARGET_IS_W2K8(torture)) {
223                 CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
224                 torture_warning(torture, "Target has bug validating lock flags "
225                                          "parameter.\n");
226         } else {
227                 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
228         }
229
230         torture_comment(torture, "Test return error when 2 locks are "
231                                  "requested\n");
232
233         lck.in.lock_count       = 2;
234         lck.in.reserved         = 0;
235         lck.in.file.handle      = h;
236         el[0].offset            = 9999;
237         el[0].length            = 1;
238         el[0].reserved          = 0x00000000;
239         el[1].offset            = 9999;
240         el[1].length            = 1;
241         el[1].reserved          = 0x00000000;
242
243         lck.in.lock_count       = 2;
244         el[0].flags             = 0;
245         el[1].flags             = SMB2_LOCK_FLAG_UNLOCK;
246         status = smb2_lock(tree, &lck);
247         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
248
249         lck.in.lock_count       = 2;
250         el[0].flags             = 0;
251         el[1].flags             = 0;
252         status = smb2_lock(tree, &lck);
253         if (TARGET_IS_W2K8(torture)) {
254                 CHECK_STATUS(status, NT_STATUS_OK);
255                 torture_warning(torture, "Target has bug validating lock flags "
256                                          "parameter.\n");
257         } else {
258                 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
259         }
260
261         lck.in.lock_count       = 2;
262         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
263         el[1].flags             = 0;
264         status = smb2_lock(tree, &lck);
265         if (TARGET_IS_W2K8(torture)) {
266                 CHECK_STATUS(status, NT_STATUS_OK);
267                 torture_warning(torture, "Target has bug validating lock flags "
268                                          "parameter.\n");
269         } else {
270                 CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
271         }
272
273         lck.in.lock_count       = 1;
274         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
275         status = smb2_lock(tree, &lck);
276         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
277
278         lck.in.lock_count       = 1;
279         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
280         status = smb2_lock(tree, &lck);
281         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
282
283         lck.in.lock_count       = 1;
284         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
285         status = smb2_lock(tree, &lck);
286         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
287
288         lck.in.lock_count       = 1;
289         el[0].flags             = SMB2_LOCK_FLAG_SHARED;
290         status = smb2_lock(tree, &lck);
291         CHECK_STATUS(status, NT_STATUS_OK);
292
293         status = smb2_lock(tree, &lck);
294         CHECK_STATUS(status, NT_STATUS_OK);
295
296         lck.in.lock_count       = 2;
297         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
298         el[1].flags             = SMB2_LOCK_FLAG_UNLOCK;
299         status = smb2_lock(tree, &lck);
300         CHECK_STATUS(status, NT_STATUS_OK);
301
302         lck.in.lock_count       = 1;
303         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
304         status = smb2_lock(tree, &lck);
305         CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
306
307 done:
308         return ret;
309 }
310
311 struct test_lock_read_write_state {
312         const char *fname;
313         uint32_t lock_flags;
314         NTSTATUS write_h1_status;
315         NTSTATUS read_h1_status;
316         NTSTATUS write_h2_status;
317         NTSTATUS read_h2_status;
318 };
319
320 static bool test_lock_read_write(struct torture_context *torture,
321                                  struct smb2_tree *tree,
322                                  struct test_lock_read_write_state *s)
323 {
324         bool ret = true;
325         NTSTATUS status;
326         struct smb2_handle h1, h2;
327         uint8_t buf[200];
328         struct smb2_lock lck;
329         struct smb2_create cr;
330         struct smb2_write wr;
331         struct smb2_read rd;
332         struct smb2_lock_element el[1];
333
334         lck.in.locks            = el;
335
336         ZERO_STRUCT(buf);
337
338         status = torture_smb2_testfile(tree, s->fname, &h1);
339         CHECK_STATUS(status, NT_STATUS_OK);
340
341         status = smb2_util_write(tree, h1, buf, 0, ARRAY_SIZE(buf));
342         CHECK_STATUS(status, NT_STATUS_OK);
343
344         lck.in.lock_count       = 0x0001;
345         lck.in.reserved         = 0x00000000;
346         lck.in.file.handle      = h1;
347         el[0].offset            = 0;
348         el[0].length            = ARRAY_SIZE(buf)/2;
349         el[0].reserved          = 0x00000000;
350         el[0].flags             = s->lock_flags;
351         status = smb2_lock(tree, &lck);
352         CHECK_STATUS(status, NT_STATUS_OK);
353         CHECK_VALUE(lck.out.reserved, 0);
354
355         lck.in.lock_count       = 0x0001;
356         lck.in.reserved         = 0x00000000;
357         lck.in.file.handle      = h1;
358         el[0].offset            = ARRAY_SIZE(buf)/2;
359         el[0].length            = ARRAY_SIZE(buf)/2;
360         el[0].reserved          = 0x00000000;
361         el[0].flags             = s->lock_flags;
362         status = smb2_lock(tree, &lck);
363         CHECK_STATUS(status, NT_STATUS_OK);
364         CHECK_VALUE(lck.out.reserved, 0);
365
366         ZERO_STRUCT(cr);
367         cr.in.oplock_level = 0;
368         cr.in.desired_access = SEC_RIGHTS_FILE_ALL;
369         cr.in.file_attributes   = FILE_ATTRIBUTE_NORMAL;
370         cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
371         cr.in.share_access = 
372                 NTCREATEX_SHARE_ACCESS_DELETE|
373                 NTCREATEX_SHARE_ACCESS_READ|
374                 NTCREATEX_SHARE_ACCESS_WRITE;
375         cr.in.create_options = 0;
376         cr.in.fname = s->fname;
377
378         status = smb2_create(tree, tree, &cr);
379         CHECK_STATUS(status, NT_STATUS_OK);
380
381         h2 = cr.out.file.handle;
382
383         ZERO_STRUCT(wr);
384         wr.in.file.handle = h1;
385         wr.in.offset      = ARRAY_SIZE(buf)/2;
386         wr.in.data        = data_blob_const(buf, ARRAY_SIZE(buf)/2);
387
388         status = smb2_write(tree, &wr);
389         CHECK_STATUS(status, s->write_h1_status);
390
391         ZERO_STRUCT(rd);
392         rd.in.file.handle = h1;
393         rd.in.offset      = ARRAY_SIZE(buf)/2;
394         rd.in.length      = ARRAY_SIZE(buf)/2;
395
396         status = smb2_read(tree, tree, &rd);
397         CHECK_STATUS(status, s->read_h1_status);
398
399         ZERO_STRUCT(wr);
400         wr.in.file.handle = h2;
401         wr.in.offset      = ARRAY_SIZE(buf)/2;
402         wr.in.data        = data_blob_const(buf, ARRAY_SIZE(buf)/2);
403
404         status = smb2_write(tree, &wr);
405         CHECK_STATUS(status, s->write_h2_status);
406
407         ZERO_STRUCT(rd);
408         rd.in.file.handle = h2;
409         rd.in.offset      = ARRAY_SIZE(buf)/2;
410         rd.in.length      = ARRAY_SIZE(buf)/2;
411
412         status = smb2_read(tree, tree, &rd);
413         CHECK_STATUS(status, s->read_h2_status);
414
415         lck.in.lock_count       = 0x0001;
416         lck.in.reserved         = 0x00000000;
417         lck.in.file.handle      = h1;
418         el[0].offset            = ARRAY_SIZE(buf)/2;
419         el[0].length            = ARRAY_SIZE(buf)/2;
420         el[0].reserved          = 0x00000000;
421         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
422         status = smb2_lock(tree, &lck);
423         CHECK_STATUS(status, NT_STATUS_OK);
424         CHECK_VALUE(lck.out.reserved, 0);
425
426         ZERO_STRUCT(wr);
427         wr.in.file.handle = h2;
428         wr.in.offset      = ARRAY_SIZE(buf)/2;
429         wr.in.data        = data_blob_const(buf, ARRAY_SIZE(buf)/2);
430
431         status = smb2_write(tree, &wr);
432         CHECK_STATUS(status, NT_STATUS_OK);
433
434         ZERO_STRUCT(rd);
435         rd.in.file.handle = h2;
436         rd.in.offset      = ARRAY_SIZE(buf)/2;
437         rd.in.length      = ARRAY_SIZE(buf)/2;
438
439         status = smb2_read(tree, tree, &rd);
440         CHECK_STATUS(status, NT_STATUS_OK);
441
442 done:
443         return ret;
444 }
445
446 static bool test_lock_rw_none(struct torture_context *torture,
447                               struct smb2_tree *tree)
448 {
449         struct test_lock_read_write_state s = {
450                 .fname                  = "lock_rw_none.dat",
451                 .lock_flags             = SMB2_LOCK_FLAG_NONE,
452                 .write_h1_status        = NT_STATUS_FILE_LOCK_CONFLICT,
453                 .read_h1_status         = NT_STATUS_OK,
454                 .write_h2_status        = NT_STATUS_FILE_LOCK_CONFLICT,
455                 .read_h2_status         = NT_STATUS_OK,
456         };
457
458         if (!TARGET_IS_W2K8(torture)) {
459                 torture_skip(torture, "RW-NONE tests the behavior of a "
460                              "NONE-type lock, which is the same as a SHARED "
461                              "lock but is granted due to a bug in W2K8.  If "
462                              "target is not W2K8 we skip this test.\n");
463         }
464
465         return test_lock_read_write(torture, tree, &s);
466 }
467
468 static bool test_lock_rw_shared(struct torture_context *torture,
469                                 struct smb2_tree *tree)
470 {
471         struct test_lock_read_write_state s = {
472                 .fname                  = "lock_rw_shared.dat",
473                 .lock_flags             = SMB2_LOCK_FLAG_SHARED,
474                 .write_h1_status        = NT_STATUS_FILE_LOCK_CONFLICT,
475                 .read_h1_status         = NT_STATUS_OK,
476                 .write_h2_status        = NT_STATUS_FILE_LOCK_CONFLICT,
477                 .read_h2_status         = NT_STATUS_OK,
478         };
479
480         return test_lock_read_write(torture, tree, &s);
481 }
482
483 static bool test_lock_rw_exclusive(struct torture_context *torture,
484                                    struct smb2_tree *tree)
485 {
486         struct test_lock_read_write_state s = {
487                 .fname                  = "lock_rw_exclusive.dat",
488                 .lock_flags             = SMB2_LOCK_FLAG_EXCLUSIVE,
489                 .write_h1_status        = NT_STATUS_OK,
490                 .read_h1_status         = NT_STATUS_OK,
491                 .write_h2_status        = NT_STATUS_FILE_LOCK_CONFLICT,
492                 .read_h2_status         = NT_STATUS_FILE_LOCK_CONFLICT,
493         };
494
495         return test_lock_read_write(torture, tree, &s);
496 }
497
498 static bool test_lock_auto_unlock(struct torture_context *torture,
499                                   struct smb2_tree *tree)
500 {
501         bool ret = true;
502         NTSTATUS status;
503         struct smb2_handle h;
504         uint8_t buf[200];
505         struct smb2_lock lck;
506         struct smb2_lock_element el[2];
507
508         ZERO_STRUCT(buf);
509
510         status = torture_smb2_testfile(tree, "autounlock.txt", &h);
511         CHECK_STATUS(status, NT_STATUS_OK);
512
513         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
514         CHECK_STATUS(status, NT_STATUS_OK);
515
516         ZERO_STRUCT(lck);
517         lck.in.locks            = el;
518         lck.in.lock_count       = 0x0001;
519         lck.in.file.handle      = h;
520         el[0].offset            = 0;
521         el[0].length            = 1;
522         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
523                                   SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
524         status = smb2_lock(tree, &lck);
525         CHECK_STATUS(status, NT_STATUS_OK);
526
527         status = smb2_lock(tree, &lck);
528         CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
529
530         status = smb2_lock(tree, &lck);
531         if (TARGET_IS_W2K8(torture)) {
532                 CHECK_STATUS(status, NT_STATUS_OK);
533                 torture_warning(torture, "Target has \"pretty please\" bug. "
534                                 "Every other contending lock request "
535                                 "succeeds.");
536         } else {
537                 CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
538         }
539
540         status = smb2_lock(tree, &lck);
541         CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
542
543 done:
544         return ret;
545 }
546
547
548 /* basic testing of SMB2 locking
549 */
550 struct torture_suite *torture_smb2_lock_init(void)
551 {
552         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "LOCK");
553
554         torture_suite_add_1smb2_test(suite, "VALID-REQUEST", test_valid_request);
555         torture_suite_add_1smb2_test(suite, "RW-NONE", test_lock_rw_none);
556         torture_suite_add_1smb2_test(suite, "RW-SHARED", test_lock_rw_shared);
557         torture_suite_add_1smb2_test(suite, "RW-EXCLUSIVE", test_lock_rw_exclusive);
558         torture_suite_add_1smb2_test(suite, "AUTO-UNLOCK", test_lock_auto_unlock);
559
560         suite->description = talloc_strdup(suite, "SMB2-LOCK tests");
561
562         return suite;
563 }
564