This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / intermezzo / presto.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Author: Peter J. Braam <braam@clusterfs.com>
5  *  Copyright (C) 1998 Stelias Computing Inc
6  *  Copyright (C) 1999 Red Hat Inc.
7  *
8  *   This file is part of InterMezzo, http://www.inter-mezzo.org.
9  *
10  *   InterMezzo is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   InterMezzo is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with InterMezzo; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * This file implements basic routines supporting the semantics
24  */
25 #include <linux/types.h>
26 #include <linux/kernel.h>
27 #include <linux/sched.h>
28 #include <linux/fs.h>
29 #include <linux/namei.h>
30 #include <linux/stat.h>
31 #include <linux/errno.h>
32 #include <linux/vmalloc.h>
33 #include <linux/slab.h>
34 #include <asm/segment.h>
35 #include <asm/uaccess.h>
36 #include <linux/string.h>
37
38 #include "intermezzo_fs.h"
39 #include "intermezzo_psdev.h"
40
41 int presto_walk(const char *name, struct nameidata *nd)
42 {
43         int err;
44         /* we do not follow symlinks to support symlink operations 
45            correctly. The vfs should always hand us resolved dentries
46            so we should not be required to use LOOKUP_FOLLOW. At the
47            reintegrating end, lento again should be working with the 
48            resolved pathname and not the symlink. SHP
49            XXX: This code implies that direct symlinks do not work. SHP
50         */
51         unsigned int flags = 0; //LOOKUP_POSITIVE;
52
53         ENTRY;
54         err = path_lookup(name, flags, nd);
55         return err;
56 }
57
58
59 /* find the presto minor device for this inode */
60 int presto_i2m(struct inode *inode)
61 {
62         struct presto_cache *cache;
63         ENTRY;
64         cache = presto_get_cache(inode);
65         CDEBUG(D_PSDEV, "\n");
66         if ( !cache ) {
67                 CERROR("PRESTO: BAD: cannot find cache for dev %s, ino %ld\n",
68                        inode->i_sb->s_id, inode->i_ino);
69                 EXIT;
70                 return -1;
71         }
72         EXIT;
73         return cache->cache_psdev->uc_minor;
74 }
75
76 inline int presto_f2m(struct presto_file_set *fset)
77 {
78         return fset->fset_cache->cache_psdev->uc_minor;
79
80 }
81
82 inline int presto_c2m(struct presto_cache *cache)
83 {
84         return cache->cache_psdev->uc_minor;
85
86 }
87
88 /* XXX check this out */
89 struct presto_file_set *presto_path2fileset(const char *name)
90 {
91         struct nameidata nd;
92         struct presto_file_set *fileset;
93         int error;
94         ENTRY;
95
96         error = presto_walk(name, &nd);
97         if (!error) { 
98 #if 0
99                 error = do_revalidate(nd.dentry);
100 #endif
101                 if (!error) 
102                         fileset = presto_fset(nd.dentry); 
103                 path_release(&nd); 
104                 EXIT;
105         } else 
106                 fileset = ERR_PTR(error);
107
108         EXIT;
109         return fileset;
110 }
111
112 /* check a flag on this dentry or fset root.  Semantics:
113    - most flags: test if it is set
114    - PRESTO_ATTR, PRESTO_DATA return 1 if PRESTO_FSETINSYNC is set
115 */
116 int presto_chk(struct dentry *dentry, int flag)
117 {
118         int minor;
119         struct presto_file_set *fset = presto_fset(dentry);
120
121         ENTRY;
122         minor = presto_i2m(dentry->d_inode);
123         if ( izo_channels[minor].uc_no_filter ) {
124                 EXIT;
125                 return ~0;
126         }
127
128         /* if the fileset is in sync DATA and ATTR are OK */
129         if ( fset &&
130              (flag == PRESTO_ATTR || flag == PRESTO_DATA) &&
131              (fset->fset_flags & FSET_INSYNC) ) {
132                 CDEBUG(D_INODE, "fset in sync (ino %ld)!\n",
133                        fset->fset_dentry->d_inode->i_ino);
134                 EXIT;
135                 return 1;
136         }
137
138         EXIT;
139         return (presto_d2d(dentry)->dd_flags & flag);
140 }
141
142 /* set a bit in the dentry flags */
143 void presto_set(struct dentry *dentry, int flag)
144 {
145         ENTRY;
146         if ( dentry->d_inode ) {
147                 CDEBUG(D_INODE, "SET ino %ld, flag %x\n",
148                        dentry->d_inode->i_ino, flag);
149         }
150         if ( presto_d2d(dentry) == NULL) {
151                 CERROR("dentry without d_fsdata in presto_set: %p: %*s", dentry,
152                                 dentry->d_name.len, dentry->d_name.name);
153                 BUG();
154         }
155         presto_d2d(dentry)->dd_flags |= flag;
156         EXIT;
157 }
158
159 /* given a path: complete the closes on the fset */
160 int lento_complete_closes(char *path)
161 {
162         struct nameidata nd;
163         struct dentry *dentry;
164         int error;
165         struct presto_file_set *fset;
166         ENTRY;
167
168         error = presto_walk(path, &nd);
169         if (error) {
170                 EXIT;
171                 return error;
172         }
173
174         dentry = nd.dentry;
175
176         error = -ENXIO;
177         if ( !presto_ispresto(dentry->d_inode) ) {
178                 EXIT;
179                 goto out_complete;
180         }
181         
182         fset = presto_fset(dentry);
183         error = -EINVAL;
184         if ( !fset ) {
185                 CERROR("No fileset!\n");
186                 EXIT;
187                 goto out_complete;
188         }
189         
190         /* transactions and locking are internal to this function */ 
191         error = presto_complete_lml(fset);
192         
193         EXIT;
194  out_complete:
195         path_release(&nd); 
196         return error;
197 }       
198
199 #if 0
200 /* given a path: write a close record and cancel an LML record, finally
201    call truncate LML.  Lento is doing this so it goes in with uid/gid's 
202    root. 
203 */ 
204 int lento_cancel_lml(char *path, 
205                      __u64 lml_offset, 
206                      __u64 remote_ino, 
207                      __u32 remote_generation,
208                      __u32 remote_version, 
209                      struct lento_vfs_context *info)
210 {
211         struct nameidata nd;
212         struct rec_info rec;
213         struct dentry *dentry;
214         int error;
215         struct presto_file_set *fset;
216         void *handle; 
217         struct presto_version new_ver;
218         ENTRY;
219
220
221         error = presto_walk(path, &nd);
222         if (error) {
223                 EXIT;
224                 return error;
225         }
226         dentry = nd.dentry;
227
228         error = -ENXIO;
229         if ( !presto_ispresto(dentry->d_inode) ) {
230                 EXIT;
231                 goto out_cancel_lml;
232         }
233         
234         fset = presto_fset(dentry);
235
236         error=-EINVAL;
237         if (fset==NULL) {
238                 CERROR("No fileset!\n");
239                 EXIT;
240                 goto out_cancel_lml;
241         }
242         
243         /* this only requires a transaction below which is automatic */
244         handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_RELEASE); 
245         if ( IS_ERR(handle) ) {
246                 error = -ENOMEM; 
247                 EXIT; 
248                 goto out_cancel_lml; 
249         } 
250         
251         if (info->flags & LENTO_FL_CANCEL_LML) {
252                 error = presto_clear_lml_close(fset, lml_offset);
253                 if ( error ) {
254                         presto_trans_commit(fset, handle);
255                         EXIT; 
256                         goto out_cancel_lml;
257                 }
258         }
259
260
261         if (info->flags & LENTO_FL_WRITE_KML) {
262                 presto_getversion(&new_ver, dentry->d_inode);
263                 error = presto_journal_close(&rec, fset, NULL, dentry,
264                                              &new_ver);
265                 if ( error ) {
266                         EXIT; 
267                         presto_trans_commit(fset, handle);
268                         goto out_cancel_lml;
269                 }
270         }
271
272         if (info->flags & LENTO_FL_WRITE_EXPECT) {
273                 error = presto_write_last_rcvd(&rec, fset, info); 
274                 if ( error < 0 ) {
275                         EXIT; 
276                         presto_trans_commit(fset, handle);
277                         goto out_cancel_lml;
278                 }
279         }
280
281         presto_trans_commit(fset, handle);
282
283         if (info->flags & LENTO_FL_CANCEL_LML) {
284             presto_truncate_lml(fset); 
285         }
286                 
287
288  out_cancel_lml:
289         EXIT;
290         path_release(&nd); 
291         return error;
292 }       
293 #endif 
294
295 /* given a dentry, operate on the flags in its dentry.  Used by downcalls */
296 int izo_mark_dentry(struct dentry *dentry, int and_flag, int or_flag, 
297                        int *res)
298 {
299         int error = 0;
300
301         if (presto_d2d(dentry) == NULL) {
302                 CERROR("InterMezzo: no ddata for inode %ld in %s\n",
303                        dentry->d_inode->i_ino, __FUNCTION__);
304                 return -EINVAL;
305         }
306
307         CDEBUG(D_INODE, "inode: %ld, and flag %x, or flag %x, dd_flags %x\n",
308                dentry->d_inode->i_ino, and_flag, or_flag,
309                presto_d2d(dentry)->dd_flags);
310
311         presto_d2d(dentry)->dd_flags &= and_flag;
312         presto_d2d(dentry)->dd_flags |= or_flag;
313         if (res) 
314                 *res = presto_d2d(dentry)->dd_flags;
315
316         return error;
317 }
318
319 /* given a path, operate on the flags in its cache.  Used by mark_ioctl */
320 int izo_mark_cache(struct dentry *dentry, int and_flag, int or_flag, 
321                    int *res)
322 {
323         struct presto_cache *cache;
324
325         if (presto_d2d(dentry) == NULL) {
326                 CERROR("InterMezzo: no ddata for inode %ld in %s\n",
327                        dentry->d_inode->i_ino, __FUNCTION__);
328                 return -EINVAL;
329         }
330
331         CDEBUG(D_INODE, "inode: %ld, and flag %x, or flag %x, dd_flags %x\n",
332                dentry->d_inode->i_ino, and_flag, or_flag,
333                presto_d2d(dentry)->dd_flags);
334
335         cache = presto_get_cache(dentry->d_inode);
336         if ( !cache ) {
337                 CERROR("PRESTO: BAD: cannot find cache in izo_mark_cache\n");
338                 return -EBADF;
339         }
340
341         cache->cache_flags &= and_flag;
342         cache->cache_flags |= or_flag;
343         if (res)
344                 *res = (int)cache->cache_flags;
345
346         return 0;
347 }
348
349 int presto_set_max_kml_size(const char *path, unsigned long max_size)
350 {
351         struct presto_file_set *fset;
352
353         ENTRY;
354
355         fset = presto_path2fileset(path);
356         if (IS_ERR(fset)) {
357                 EXIT;
358                 return PTR_ERR(fset);
359         }
360
361         fset->kml_truncate_size = max_size;
362         CDEBUG(D_CACHE, "KML truncate size set to %lu bytes for fset %s.\n",
363                max_size, path);
364
365         EXIT;
366         return 0;
367 }
368
369 int izo_mark_fset(struct dentry *dentry, int and_flag, int or_flag, 
370                   int * res)
371 {
372         struct presto_file_set *fset;
373         
374         fset = presto_fset(dentry);
375         if ( !fset ) {
376                 CERROR("PRESTO: BAD: cannot find cache in izo_mark_cache\n");
377                 make_bad_inode(dentry->d_inode);
378                 return -EBADF;
379         }
380         fset->fset_flags &= and_flag;
381         fset->fset_flags |= or_flag;
382         if (res)
383                 *res = (int)fset->fset_flags;
384
385         return 0;
386 }
387
388 /* talk to Lento about the permit */
389 static int presto_permit_upcall(struct dentry *dentry)
390 {
391         int rc;
392         char *path, *buffer;
393         int pathlen;
394         int minor;
395         int fsetnamelen;
396         struct presto_file_set *fset = NULL;
397
398         ENTRY;
399
400         if ( (minor = presto_i2m(dentry->d_inode)) < 0) {
401                 EXIT;
402                 return -EINVAL;
403         }
404
405         fset = presto_fset(dentry);
406         if (!fset) {
407                 EXIT;
408                 return -ENOTCONN;
409         }
410         
411         if ( !presto_lento_up(minor) ) {
412                 if ( fset->fset_flags & FSET_STEAL_PERMIT ) {
413                         EXIT;
414                         return 0;
415                 } else {
416                         EXIT;
417                         return -ENOTCONN;
418                 }
419         }
420
421         PRESTO_ALLOC(buffer, PAGE_SIZE);
422         if ( !buffer ) {
423                 CERROR("PRESTO: out of memory!\n");
424                 EXIT;
425                 return -ENOMEM;
426         }
427         path = presto_path(dentry, fset->fset_dentry, buffer, PAGE_SIZE);
428         pathlen = MYPATHLEN(buffer, path);
429         fsetnamelen = strlen(fset->fset_name); 
430         rc = izo_upc_permit(minor, dentry, pathlen, path, fset->fset_name);
431         PRESTO_FREE(buffer, PAGE_SIZE);
432         EXIT;
433         return rc;
434 }
435
436 /* get a write permit for the fileset of this inode
437  *  - if this returns a negative value there was an error
438  *  - if 0 is returned the permit was already in the kernel -- or --
439  *    Lento gave us the permit without reintegration
440  *  - lento returns the number of records it reintegrated 
441  *
442  * Note that if this fileset has branches, a permit will -never- to a normal
443  * process for writing in the data area (ie, outside of .intermezzo)
444  */
445 int presto_get_permit(struct inode * inode)
446 {
447         struct dentry *de;
448         struct presto_file_set *fset;
449         int minor = presto_i2m(inode);
450         int rc = 0;
451
452         ENTRY;
453         if (minor < 0) {
454                 EXIT;
455                 return -1;
456         }
457
458         if ( ISLENTO(minor) ) {
459                 EXIT;
460                 return 0;
461         }
462
463         if (list_empty(&inode->i_dentry)) {
464                 CERROR("No alias for inode %d\n", (int) inode->i_ino);
465                 EXIT;
466                 return -EINVAL;
467         }
468
469         de = list_entry(inode->i_dentry.next, struct dentry, d_alias);
470
471         if (presto_chk(de, PRESTO_DONT_JOURNAL)) {
472                 EXIT;
473                 return 0;
474         }
475
476         fset = presto_fset(de);
477         if ( !fset ) {
478                 CERROR("Presto: no fileset in presto_get_permit!\n");
479                 EXIT;
480                 return -EINVAL;
481         }
482
483         if (fset->fset_flags & FSET_HAS_BRANCHES) {
484                 EXIT;
485                 return -EROFS;
486         }
487
488         spin_lock(&fset->fset_permit_lock);
489         if (fset->fset_flags & FSET_HASPERMIT) {
490                 fset->fset_permit_count++;
491                 CDEBUG(D_INODE, "permit count now %d, inode %lx\n", 
492                        fset->fset_permit_count, inode->i_ino);
493                 spin_unlock(&fset->fset_permit_lock);
494                 EXIT;
495                 return 0;
496         }
497
498         /* Allow reintegration to proceed without locks -SHP */
499         fset->fset_permit_upcall_count++;
500         if (fset->fset_permit_upcall_count == 1) {
501                 spin_unlock(&fset->fset_permit_lock);
502                 rc = presto_permit_upcall(fset->fset_dentry);
503                 spin_lock(&fset->fset_permit_lock);
504                 fset->fset_permit_upcall_count--;
505                 if (rc == 0) {
506                         izo_mark_fset(fset->fset_dentry, ~0, FSET_HASPERMIT,
507                                       NULL);
508                         fset->fset_permit_count++;
509                 } else if (rc == ENOTCONN) {
510                         CERROR("InterMezzo: disconnected operation. stealing permit.\n");
511                         izo_mark_fset(fset->fset_dentry, ~0, FSET_HASPERMIT,
512                                       NULL);
513                         fset->fset_permit_count++;
514                         /* set a disconnected flag here to stop upcalls */
515                         rc = 0;
516                 } else {
517                         CERROR("InterMezzo: presto_permit_upcall failed: %d\n", rc);
518                         rc = -EROFS;
519                         /* go to sleep here and try again? */
520                 }
521                 wake_up_interruptible(&fset->fset_permit_queue);
522         } else {
523                 /* Someone is already doing an upcall; go to sleep. */
524                 DECLARE_WAITQUEUE(wait, current);
525
526                 spin_unlock(&fset->fset_permit_lock);
527                 add_wait_queue(&fset->fset_permit_queue, &wait);
528                 while (1) {
529                         set_current_state(TASK_INTERRUPTIBLE);
530
531                         spin_lock(&fset->fset_permit_lock);
532                         if (fset->fset_permit_upcall_count == 0)
533                                 break;
534                         spin_unlock(&fset->fset_permit_lock);
535
536                         if (signal_pending(current)) {
537                                 remove_wait_queue(&fset->fset_permit_queue,
538                                                   &wait);
539                                 return -ERESTARTSYS;
540                         }
541                         schedule();
542                 }
543                 remove_wait_queue(&fset->fset_permit_queue, &wait);
544                 /* We've been woken up: do we have the permit? */
545                 if (fset->fset_flags & FSET_HASPERMIT)
546                         /* FIXME: Is this the right thing? */
547                         rc = -EAGAIN;
548         }
549
550         CDEBUG(D_INODE, "permit count now %d, ino %ld (likely 1), "
551                "rc %d\n", fset->fset_permit_count, inode->i_ino, rc);
552         spin_unlock(&fset->fset_permit_lock);
553         EXIT;
554         return rc;
555 }
556
557 int presto_put_permit(struct inode * inode)
558 {
559         struct dentry *de;
560         struct presto_file_set *fset;
561         int minor = presto_i2m(inode);
562
563         ENTRY;
564         if (minor < 0) {
565                 EXIT;
566                 return -1;
567         }
568
569         if ( ISLENTO(minor) ) {
570                 EXIT;
571                 return 0;
572         }
573
574         if (list_empty(&inode->i_dentry)) {
575                 CERROR("No alias for inode %d\n", (int) inode->i_ino);
576                 EXIT;
577                 return -1;
578         }
579
580         de = list_entry(inode->i_dentry.next, struct dentry, d_alias);
581
582         fset = presto_fset(de);
583         if ( !fset ) {
584                 CERROR("InterMezzo: no fileset in %s!\n", __FUNCTION__);
585                 EXIT;
586                 return -1;
587         }
588
589         if (presto_chk(de, PRESTO_DONT_JOURNAL)) {
590                 EXIT;
591                 return 0;
592         }
593
594         spin_lock(&fset->fset_permit_lock);
595         if (fset->fset_flags & FSET_HASPERMIT) {
596                 if (fset->fset_permit_count > 0)
597                         fset->fset_permit_count--;
598                 else
599                         CERROR("Put permit while permit count is 0, "
600                                "inode %ld!\n", inode->i_ino); 
601         } else {
602                 fset->fset_permit_count = 0;
603                 CERROR("InterMezzo: put permit while no permit, inode %ld, "
604                        "flags %x!\n", inode->i_ino, fset->fset_flags);
605         }
606
607         CDEBUG(D_INODE, "permit count now %d, inode %ld\n",
608                fset->fset_permit_count, inode->i_ino);
609
610         if (fset->fset_flags & FSET_PERMIT_WAITING &&
611             fset->fset_permit_count == 0) {
612                 CDEBUG(D_INODE, "permit count now 0, ino %ld, wake sleepers\n",
613                        inode->i_ino);
614                 wake_up_interruptible(&fset->fset_permit_queue);
615         }
616         spin_unlock(&fset->fset_permit_lock);
617
618         EXIT;
619         return 0;
620 }
621
622 void presto_getversion(struct presto_version * presto_version,
623                        struct inode * inode)
624 {
625         presto_version->pv_mtime_sec = inode->i_mtime.tv_sec;
626         presto_version->pv_mtime_nsec = inode->i_mtime.tv_nsec;
627         presto_version->pv_ctime_sec = inode->i_ctime.tv_sec;
628         presto_version->pv_ctime_nsec = inode->i_ctime.tv_nsec;
629         presto_version->pv_size  = (__u64)inode->i_size;
630 }
631
632
633 /* If uuid is non-null, it is the uuid of the peer that's making the revocation
634  * request.  If it is null, this request was made locally, without external
635  * pressure to give up the permit.  This most often occurs when a client
636  * starts up.
637  *
638  * FIXME: this function needs to be refactored slightly once we start handling
639  * multiple clients.
640  */
641 int izo_revoke_permit(struct dentry *dentry, __u8 uuid[16])
642 {
643         struct presto_file_set *fset; 
644         DECLARE_WAITQUEUE(wait, current);
645         int minor, rc;
646
647         ENTRY;
648
649         minor = presto_i2m(dentry->d_inode);
650         if (minor < 0) {
651                 EXIT;
652                 return -ENODEV;
653         }
654
655         fset = presto_fset(dentry);
656         if (fset == NULL) {
657                 EXIT;
658                 return -ENODEV;
659         }
660
661         spin_lock(&fset->fset_permit_lock);
662         if (fset->fset_flags & FSET_PERMIT_WAITING) {
663                 CERROR("InterMezzo: Two processes are waiting on the same permit--this not yet supported!  Aborting this particular permit request...\n");
664                 EXIT;
665                 spin_unlock(&fset->fset_permit_lock);
666                 return -EINVAL;
667         }
668
669         if (fset->fset_permit_count == 0)
670                 goto got_permit;
671
672         /* Something is still using this permit.  Mark that we're waiting for it
673          * and go to sleep. */
674         rc = izo_mark_fset(dentry, ~0, FSET_PERMIT_WAITING, NULL);
675         spin_unlock(&fset->fset_permit_lock);
676         if (rc < 0) {
677                 EXIT;
678                 return rc;
679         }
680
681         add_wait_queue(&fset->fset_permit_queue, &wait);
682         while (1) {
683                 set_current_state(TASK_INTERRUPTIBLE);
684
685                 spin_lock(&fset->fset_permit_lock);
686                 if (fset->fset_permit_count == 0)
687                         break;
688                 spin_unlock(&fset->fset_permit_lock);
689
690                 if (signal_pending(current)) {
691                         /* FIXME: there must be a better thing to return... */
692                         remove_wait_queue(&fset->fset_permit_queue, &wait);
693                         EXIT;
694                         return -ERESTARTSYS;
695                 }
696
697                 /* FIXME: maybe there should be a timeout here. */
698
699                 schedule();
700         }
701
702         remove_wait_queue(&fset->fset_permit_queue, &wait);
703  got_permit:
704         /* By this point fset->fset_permit_count is zero and we're holding the
705          * lock. */
706         CDEBUG(D_CACHE, "InterMezzo: releasing permit inode %ld\n",
707                dentry->d_inode->i_ino);
708
709         if (uuid != NULL) {
710                 rc = izo_upc_revoke_permit(minor, fset->fset_name, uuid);
711                 if (rc < 0) {
712                         spin_unlock(&fset->fset_permit_lock);
713                         EXIT;
714                         return rc;
715                 }
716         }
717
718         izo_mark_fset(fset->fset_dentry, ~FSET_PERMIT_WAITING, 0, NULL);
719         izo_mark_fset(fset->fset_dentry, ~FSET_HASPERMIT, 0, NULL);
720         spin_unlock(&fset->fset_permit_lock);
721         EXIT;
722         return 0;
723 }
724
725 inline int presto_is_read_only(struct presto_file_set * fset)
726 {
727         int minor, mask;
728         struct presto_cache *cache = fset->fset_cache;
729
730         minor= cache->cache_psdev->uc_minor;
731         mask= (ISLENTO(minor)? FSET_LENTO_RO : FSET_CLIENT_RO);
732         if ( fset->fset_flags & mask )
733                 return 1;
734         mask= (ISLENTO(minor)? CACHE_LENTO_RO : CACHE_CLIENT_RO);
735         return  ((cache->cache_flags & mask)? 1 : 0);
736 }