GNU Linux-libre 4.19.286-gnu1
[releases.git] / fs / xfs / libxfs / xfs_dir2.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
4  * All Rights Reserved.
5  */
6 #include "xfs.h"
7 #include "xfs_fs.h"
8 #include "xfs_format.h"
9 #include "xfs_log_format.h"
10 #include "xfs_trans_resv.h"
11 #include "xfs_mount.h"
12 #include "xfs_defer.h"
13 #include "xfs_da_format.h"
14 #include "xfs_da_btree.h"
15 #include "xfs_inode.h"
16 #include "xfs_trans.h"
17 #include "xfs_inode_item.h"
18 #include "xfs_bmap.h"
19 #include "xfs_dir2.h"
20 #include "xfs_dir2_priv.h"
21 #include "xfs_ialloc.h"
22 #include "xfs_errortag.h"
23 #include "xfs_error.h"
24 #include "xfs_trace.h"
25
26 struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };
27
28 /*
29  * Convert inode mode to directory entry filetype
30  */
31 unsigned char
32 xfs_mode_to_ftype(
33         int             mode)
34 {
35         switch (mode & S_IFMT) {
36         case S_IFREG:
37                 return XFS_DIR3_FT_REG_FILE;
38         case S_IFDIR:
39                 return XFS_DIR3_FT_DIR;
40         case S_IFCHR:
41                 return XFS_DIR3_FT_CHRDEV;
42         case S_IFBLK:
43                 return XFS_DIR3_FT_BLKDEV;
44         case S_IFIFO:
45                 return XFS_DIR3_FT_FIFO;
46         case S_IFSOCK:
47                 return XFS_DIR3_FT_SOCK;
48         case S_IFLNK:
49                 return XFS_DIR3_FT_SYMLINK;
50         default:
51                 return XFS_DIR3_FT_UNKNOWN;
52         }
53 }
54
55 /*
56  * ASCII case-insensitive (ie. A-Z) support for directories that was
57  * used in IRIX.
58  */
59 STATIC xfs_dahash_t
60 xfs_ascii_ci_hashname(
61         struct xfs_name *name)
62 {
63         xfs_dahash_t    hash;
64         int             i;
65
66         for (i = 0, hash = 0; i < name->len; i++)
67                 hash = tolower(name->name[i]) ^ rol32(hash, 7);
68
69         return hash;
70 }
71
72 STATIC enum xfs_dacmp
73 xfs_ascii_ci_compname(
74         struct xfs_da_args *args,
75         const unsigned char *name,
76         int             len)
77 {
78         enum xfs_dacmp  result;
79         int             i;
80
81         if (args->namelen != len)
82                 return XFS_CMP_DIFFERENT;
83
84         result = XFS_CMP_EXACT;
85         for (i = 0; i < len; i++) {
86                 if (args->name[i] == name[i])
87                         continue;
88                 if (tolower(args->name[i]) != tolower(name[i]))
89                         return XFS_CMP_DIFFERENT;
90                 result = XFS_CMP_CASE;
91         }
92
93         return result;
94 }
95
96 static const struct xfs_nameops xfs_ascii_ci_nameops = {
97         .hashname       = xfs_ascii_ci_hashname,
98         .compname       = xfs_ascii_ci_compname,
99 };
100
101 int
102 xfs_da_mount(
103         struct xfs_mount        *mp)
104 {
105         struct xfs_da_geometry  *dageo;
106         int                     nodehdr_size;
107
108
109         ASSERT(mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT);
110         ASSERT(xfs_dir2_dirblock_bytes(&mp->m_sb) <= XFS_MAX_BLOCKSIZE);
111
112         mp->m_dir_inode_ops = xfs_dir_get_ops(mp, NULL);
113         mp->m_nondir_inode_ops = xfs_nondir_get_ops(mp, NULL);
114
115         nodehdr_size = mp->m_dir_inode_ops->node_hdr_size;
116         mp->m_dir_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
117                                     KM_SLEEP | KM_MAYFAIL);
118         mp->m_attr_geo = kmem_zalloc(sizeof(struct xfs_da_geometry),
119                                      KM_SLEEP | KM_MAYFAIL);
120         if (!mp->m_dir_geo || !mp->m_attr_geo) {
121                 kmem_free(mp->m_dir_geo);
122                 kmem_free(mp->m_attr_geo);
123                 return -ENOMEM;
124         }
125
126         /* set up directory geometry */
127         dageo = mp->m_dir_geo;
128         dageo->blklog = mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog;
129         dageo->fsblog = mp->m_sb.sb_blocklog;
130         dageo->blksize = xfs_dir2_dirblock_bytes(&mp->m_sb);
131         dageo->fsbcount = 1 << mp->m_sb.sb_dirblklog;
132
133         /*
134          * Now we've set up the block conversion variables, we can calculate the
135          * segment block constants using the geometry structure.
136          */
137         dageo->datablk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_DATA_OFFSET);
138         dageo->leafblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_LEAF_OFFSET);
139         dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET);
140         dageo->node_ents = (dageo->blksize - nodehdr_size) /
141                                 (uint)sizeof(xfs_da_node_entry_t);
142         dageo->magicpct = (dageo->blksize * 37) / 100;
143
144         /* set up attribute geometry - single fsb only */
145         dageo = mp->m_attr_geo;
146         dageo->blklog = mp->m_sb.sb_blocklog;
147         dageo->fsblog = mp->m_sb.sb_blocklog;
148         dageo->blksize = 1 << dageo->blklog;
149         dageo->fsbcount = 1;
150         dageo->node_ents = (dageo->blksize - nodehdr_size) /
151                                 (uint)sizeof(xfs_da_node_entry_t);
152         dageo->magicpct = (dageo->blksize * 37) / 100;
153
154         if (xfs_sb_version_hasasciici(&mp->m_sb))
155                 mp->m_dirnameops = &xfs_ascii_ci_nameops;
156         else
157                 mp->m_dirnameops = &xfs_default_nameops;
158
159         return 0;
160 }
161
162 void
163 xfs_da_unmount(
164         struct xfs_mount        *mp)
165 {
166         kmem_free(mp->m_dir_geo);
167         kmem_free(mp->m_attr_geo);
168 }
169
170 /*
171  * Return 1 if directory contains only "." and "..".
172  */
173 int
174 xfs_dir_isempty(
175         xfs_inode_t     *dp)
176 {
177         xfs_dir2_sf_hdr_t       *sfp;
178
179         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
180         if (dp->i_d.di_size == 0)       /* might happen during shutdown. */
181                 return 1;
182         if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
183                 return 0;
184         sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
185         return !sfp->count;
186 }
187
188 /*
189  * Validate a given inode number.
190  */
191 int
192 xfs_dir_ino_validate(
193         xfs_mount_t     *mp,
194         xfs_ino_t       ino)
195 {
196         bool            ino_ok = xfs_verify_dir_ino(mp, ino);
197
198         if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE))) {
199                 xfs_warn(mp, "Invalid inode number 0x%Lx",
200                                 (unsigned long long) ino);
201                 XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
202                 return -EFSCORRUPTED;
203         }
204         return 0;
205 }
206
207 /*
208  * Initialize a directory with its "." and ".." entries.
209  */
210 int
211 xfs_dir_init(
212         xfs_trans_t     *tp,
213         xfs_inode_t     *dp,
214         xfs_inode_t     *pdp)
215 {
216         struct xfs_da_args *args;
217         int             error;
218
219         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
220         error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino);
221         if (error)
222                 return error;
223
224         args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
225         if (!args)
226                 return -ENOMEM;
227
228         args->geo = dp->i_mount->m_dir_geo;
229         args->dp = dp;
230         args->trans = tp;
231         error = xfs_dir2_sf_create(args, pdp->i_ino);
232         kmem_free(args);
233         return error;
234 }
235
236 /*
237  * Enter a name in a directory, or check for available space.
238  * If inum is 0, only the available space test is performed.
239  */
240 int
241 xfs_dir_createname(
242         struct xfs_trans        *tp,
243         struct xfs_inode        *dp,
244         struct xfs_name         *name,
245         xfs_ino_t               inum,           /* new entry inode number */
246         xfs_extlen_t            total)          /* bmap's total block count */
247 {
248         struct xfs_da_args      *args;
249         int                     rval;
250         int                     v;              /* type-checking value */
251
252         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
253
254         if (inum) {
255                 rval = xfs_dir_ino_validate(tp->t_mountp, inum);
256                 if (rval)
257                         return rval;
258                 XFS_STATS_INC(dp->i_mount, xs_dir_create);
259         }
260
261         args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
262         if (!args)
263                 return -ENOMEM;
264
265         args->geo = dp->i_mount->m_dir_geo;
266         args->name = name->name;
267         args->namelen = name->len;
268         args->filetype = name->type;
269         args->hashval = dp->i_mount->m_dirnameops->hashname(name);
270         args->inumber = inum;
271         args->dp = dp;
272         args->total = total;
273         args->whichfork = XFS_DATA_FORK;
274         args->trans = tp;
275         args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
276         if (!inum)
277                 args->op_flags |= XFS_DA_OP_JUSTCHECK;
278
279         if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
280                 rval = xfs_dir2_sf_addname(args);
281                 goto out_free;
282         }
283
284         rval = xfs_dir2_isblock(args, &v);
285         if (rval)
286                 goto out_free;
287         if (v) {
288                 rval = xfs_dir2_block_addname(args);
289                 goto out_free;
290         }
291
292         rval = xfs_dir2_isleaf(args, &v);
293         if (rval)
294                 goto out_free;
295         if (v)
296                 rval = xfs_dir2_leaf_addname(args);
297         else
298                 rval = xfs_dir2_node_addname(args);
299
300 out_free:
301         kmem_free(args);
302         return rval;
303 }
304
305 /*
306  * If doing a CI lookup and case-insensitive match, dup actual name into
307  * args.value. Return EEXIST for success (ie. name found) or an error.
308  */
309 int
310 xfs_dir_cilookup_result(
311         struct xfs_da_args *args,
312         const unsigned char *name,
313         int             len)
314 {
315         if (args->cmpresult == XFS_CMP_DIFFERENT)
316                 return -ENOENT;
317         if (args->cmpresult != XFS_CMP_CASE ||
318                                         !(args->op_flags & XFS_DA_OP_CILOOKUP))
319                 return -EEXIST;
320
321         args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL);
322         if (!args->value)
323                 return -ENOMEM;
324
325         memcpy(args->value, name, len);
326         args->valuelen = len;
327         return -EEXIST;
328 }
329
330 /*
331  * Lookup a name in a directory, give back the inode number.
332  * If ci_name is not NULL, returns the actual name in ci_name if it differs
333  * to name, or ci_name->name is set to NULL for an exact match.
334  */
335
336 int
337 xfs_dir_lookup(
338         xfs_trans_t     *tp,
339         xfs_inode_t     *dp,
340         struct xfs_name *name,
341         xfs_ino_t       *inum,          /* out: inode number */
342         struct xfs_name *ci_name)       /* out: actual name if CI match */
343 {
344         struct xfs_da_args *args;
345         int             rval;
346         int             v;              /* type-checking value */
347         int             lock_mode;
348
349         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
350         XFS_STATS_INC(dp->i_mount, xs_dir_lookup);
351
352         /*
353          * We need to use KM_NOFS here so that lockdep will not throw false
354          * positive deadlock warnings on a non-transactional lookup path. It is
355          * safe to recurse into inode recalim in that case, but lockdep can't
356          * easily be taught about it. Hence KM_NOFS avoids having to add more
357          * lockdep Doing this avoids having to add a bunch of lockdep class
358          * annotations into the reclaim path for the ilock.
359          */
360         args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
361         args->geo = dp->i_mount->m_dir_geo;
362         args->name = name->name;
363         args->namelen = name->len;
364         args->filetype = name->type;
365         args->hashval = dp->i_mount->m_dirnameops->hashname(name);
366         args->dp = dp;
367         args->whichfork = XFS_DATA_FORK;
368         args->trans = tp;
369         args->op_flags = XFS_DA_OP_OKNOENT;
370         if (ci_name)
371                 args->op_flags |= XFS_DA_OP_CILOOKUP;
372
373         lock_mode = xfs_ilock_data_map_shared(dp);
374         if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
375                 rval = xfs_dir2_sf_lookup(args);
376                 goto out_check_rval;
377         }
378
379         rval = xfs_dir2_isblock(args, &v);
380         if (rval)
381                 goto out_free;
382         if (v) {
383                 rval = xfs_dir2_block_lookup(args);
384                 goto out_check_rval;
385         }
386
387         rval = xfs_dir2_isleaf(args, &v);
388         if (rval)
389                 goto out_free;
390         if (v)
391                 rval = xfs_dir2_leaf_lookup(args);
392         else
393                 rval = xfs_dir2_node_lookup(args);
394
395 out_check_rval:
396         if (rval == -EEXIST)
397                 rval = 0;
398         if (!rval) {
399                 *inum = args->inumber;
400                 if (ci_name) {
401                         ci_name->name = args->value;
402                         ci_name->len = args->valuelen;
403                 }
404         }
405 out_free:
406         xfs_iunlock(dp, lock_mode);
407         kmem_free(args);
408         return rval;
409 }
410
411 /*
412  * Remove an entry from a directory.
413  */
414 int
415 xfs_dir_removename(
416         struct xfs_trans        *tp,
417         struct xfs_inode        *dp,
418         struct xfs_name         *name,
419         xfs_ino_t               ino,
420         xfs_extlen_t            total)          /* bmap's total block count */
421 {
422         struct xfs_da_args      *args;
423         int                     rval;
424         int                     v;              /* type-checking value */
425
426         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
427         XFS_STATS_INC(dp->i_mount, xs_dir_remove);
428
429         args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
430         if (!args)
431                 return -ENOMEM;
432
433         args->geo = dp->i_mount->m_dir_geo;
434         args->name = name->name;
435         args->namelen = name->len;
436         args->filetype = name->type;
437         args->hashval = dp->i_mount->m_dirnameops->hashname(name);
438         args->inumber = ino;
439         args->dp = dp;
440         args->total = total;
441         args->whichfork = XFS_DATA_FORK;
442         args->trans = tp;
443
444         if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
445                 rval = xfs_dir2_sf_removename(args);
446                 goto out_free;
447         }
448
449         rval = xfs_dir2_isblock(args, &v);
450         if (rval)
451                 goto out_free;
452         if (v) {
453                 rval = xfs_dir2_block_removename(args);
454                 goto out_free;
455         }
456
457         rval = xfs_dir2_isleaf(args, &v);
458         if (rval)
459                 goto out_free;
460         if (v)
461                 rval = xfs_dir2_leaf_removename(args);
462         else
463                 rval = xfs_dir2_node_removename(args);
464 out_free:
465         kmem_free(args);
466         return rval;
467 }
468
469 /*
470  * Replace the inode number of a directory entry.
471  */
472 int
473 xfs_dir_replace(
474         struct xfs_trans        *tp,
475         struct xfs_inode        *dp,
476         struct xfs_name         *name,          /* name of entry to replace */
477         xfs_ino_t               inum,           /* new inode number */
478         xfs_extlen_t            total)          /* bmap's total block count */
479 {
480         struct xfs_da_args      *args;
481         int                     rval;
482         int                     v;              /* type-checking value */
483
484         ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
485
486         rval = xfs_dir_ino_validate(tp->t_mountp, inum);
487         if (rval)
488                 return rval;
489
490         args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS);
491         if (!args)
492                 return -ENOMEM;
493
494         args->geo = dp->i_mount->m_dir_geo;
495         args->name = name->name;
496         args->namelen = name->len;
497         args->filetype = name->type;
498         args->hashval = dp->i_mount->m_dirnameops->hashname(name);
499         args->inumber = inum;
500         args->dp = dp;
501         args->total = total;
502         args->whichfork = XFS_DATA_FORK;
503         args->trans = tp;
504
505         if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
506                 rval = xfs_dir2_sf_replace(args);
507                 goto out_free;
508         }
509
510         rval = xfs_dir2_isblock(args, &v);
511         if (rval)
512                 goto out_free;
513         if (v) {
514                 rval = xfs_dir2_block_replace(args);
515                 goto out_free;
516         }
517
518         rval = xfs_dir2_isleaf(args, &v);
519         if (rval)
520                 goto out_free;
521         if (v)
522                 rval = xfs_dir2_leaf_replace(args);
523         else
524                 rval = xfs_dir2_node_replace(args);
525 out_free:
526         kmem_free(args);
527         return rval;
528 }
529
530 /*
531  * See if this entry can be added to the directory without allocating space.
532  */
533 int
534 xfs_dir_canenter(
535         xfs_trans_t     *tp,
536         xfs_inode_t     *dp,
537         struct xfs_name *name)          /* name of entry to add */
538 {
539         return xfs_dir_createname(tp, dp, name, 0, 0);
540 }
541
542 /*
543  * Utility routines.
544  */
545
546 /*
547  * Add a block to the directory.
548  *
549  * This routine is for data and free blocks, not leaf/node blocks which are
550  * handled by xfs_da_grow_inode.
551  */
552 int
553 xfs_dir2_grow_inode(
554         struct xfs_da_args      *args,
555         int                     space,  /* v2 dir's space XFS_DIR2_xxx_SPACE */
556         xfs_dir2_db_t           *dbp)   /* out: block number added */
557 {
558         struct xfs_inode        *dp = args->dp;
559         struct xfs_mount        *mp = dp->i_mount;
560         xfs_fileoff_t           bno;    /* directory offset of new block */
561         int                     count;  /* count of filesystem blocks */
562         int                     error;
563
564         trace_xfs_dir2_grow_inode(args, space);
565
566         /*
567          * Set lowest possible block in the space requested.
568          */
569         bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE);
570         count = args->geo->fsbcount;
571
572         error = xfs_da_grow_inode_int(args, &bno, count);
573         if (error)
574                 return error;
575
576         *dbp = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)bno);
577
578         /*
579          * Update file's size if this is the data space and it grew.
580          */
581         if (space == XFS_DIR2_DATA_SPACE) {
582                 xfs_fsize_t     size;           /* directory file (data) size */
583
584                 size = XFS_FSB_TO_B(mp, bno + count);
585                 if (size > dp->i_d.di_size) {
586                         dp->i_d.di_size = size;
587                         xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
588                 }
589         }
590         return 0;
591 }
592
593 /*
594  * See if the directory is a single-block form directory.
595  */
596 int
597 xfs_dir2_isblock(
598         struct xfs_da_args      *args,
599         int                     *vp)    /* out: 1 is block, 0 is not block */
600 {
601         xfs_fileoff_t           last;   /* last file offset */
602         int                     rval;
603
604         if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
605                 return rval;
606         rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize;
607         if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize)
608                 return -EFSCORRUPTED;
609         *vp = rval;
610         return 0;
611 }
612
613 /*
614  * See if the directory is a single-leaf form directory.
615  */
616 int
617 xfs_dir2_isleaf(
618         struct xfs_da_args      *args,
619         int                     *vp)    /* out: 1 is block, 0 is not block */
620 {
621         xfs_fileoff_t           last;   /* last file offset */
622         int                     rval;
623
624         if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK)))
625                 return rval;
626         *vp = last == args->geo->leafblk + args->geo->fsbcount;
627         return 0;
628 }
629
630 /*
631  * Remove the given block from the directory.
632  * This routine is used for data and free blocks, leaf/node are done
633  * by xfs_da_shrink_inode.
634  */
635 int
636 xfs_dir2_shrink_inode(
637         struct xfs_da_args      *args,
638         xfs_dir2_db_t           db,
639         struct xfs_buf          *bp)
640 {
641         xfs_fileoff_t           bno;            /* directory file offset */
642         xfs_dablk_t             da;             /* directory file offset */
643         int                     done;           /* bunmap is finished */
644         struct xfs_inode        *dp;
645         int                     error;
646         struct xfs_mount        *mp;
647         struct xfs_trans        *tp;
648
649         trace_xfs_dir2_shrink_inode(args, db);
650
651         dp = args->dp;
652         mp = dp->i_mount;
653         tp = args->trans;
654         da = xfs_dir2_db_to_da(args->geo, db);
655
656         /* Unmap the fsblock(s). */
657         error = xfs_bunmapi(tp, dp, da, args->geo->fsbcount, 0, 0, &done);
658         if (error) {
659                 /*
660                  * ENOSPC actually can happen if we're in a removename with no
661                  * space reservation, and the resulting block removal would
662                  * cause a bmap btree split or conversion from extents to btree.
663                  * This can only happen for un-fragmented directory blocks,
664                  * since you need to be punching out the middle of an extent.
665                  * In this case we need to leave the block in the file, and not
666                  * binval it.  So the block has to be in a consistent empty
667                  * state and appropriately logged.  We don't free up the buffer,
668                  * the caller can tell it hasn't happened since it got an error
669                  * back.
670                  */
671                 return error;
672         }
673         ASSERT(done);
674         /*
675          * Invalidate the buffer from the transaction.
676          */
677         xfs_trans_binval(tp, bp);
678         /*
679          * If it's not a data block, we're done.
680          */
681         if (db >= xfs_dir2_byte_to_db(args->geo, XFS_DIR2_LEAF_OFFSET))
682                 return 0;
683         /*
684          * If the block isn't the last one in the directory, we're done.
685          */
686         if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(args->geo, db + 1, 0))
687                 return 0;
688         bno = da;
689         if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) {
690                 /*
691                  * This can't really happen unless there's kernel corruption.
692                  */
693                 return error;
694         }
695         if (db == args->geo->datablk)
696                 ASSERT(bno == 0);
697         else
698                 ASSERT(bno > 0);
699         /*
700          * Set the size to the new last block.
701          */
702         dp->i_d.di_size = XFS_FSB_TO_B(mp, bno);
703         xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
704         return 0;
705 }