1 /*
2 * linux/fs/fat/misc.c
3 *
4 * Written 1992,1993 by Werner Almesberger
5 */
6
7 #include <linux/fs.h>
8 #include <linux/msdos_fs.h>
9 #include <linux/sched.h>
10 #include <linux/kernel.h>
11 #include <linux/errno.h>
12 #include <linux/string.h>
13 #include <linux/stat.h>
14
15 #include "msbuffer.h"
16
17 #if 0
18 # define PRINTK(x) printk x
19 #else
20 # define PRINTK(x)
21 #endif
22 #define Printk(x) printk x
23
24 /* Well-known binary file extensions - of course there are many more */
25
26 static char ascii_extensions[] =
27 "TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */
28 "C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */
29 "F " "MAK" "INC" "BAS" /* programming languages */
30 "BAT" "SH " /* program code :) */
31 "INI" /* config files */
32 "PBM" "PGM" "DXF" /* graphics */
33 "TEX"; /* TeX */
34
35
36 /*
37 * fat_fs_panic reports a severe file system problem and sets the file system
38 * read-only. The file system can be made writable again by remounting it.
39 */
40
41 void fat_fs_panic(struct super_block *s,const char *msg)
42 {
43 int not_ro;
44
45 not_ro = !(s->s_flags & MS_RDONLY);
46 if (not_ro) s->s_flags |= MS_RDONLY;
47 printk("Filesystem panic (dev %s).\n %s\n", kdevname(s->s_dev), msg);
48 if (not_ro)
49 printk(" File system has been set read-only\n");
50 }
51
52
53 /*
54 * fat_is_binary selects optional text conversion based on the conversion mode
55 * and the extension part of the file name.
56 */
57
58 int fat_is_binary(char conversion,char *extension)
59 {
60 char *walk;
61
62 switch (conversion) {
63 case 'b':
64 return 1;
65 case 't':
66 return 0;
67 case 'a':
68 for (walk = ascii_extensions; *walk; walk += 3)
69 if (!strncmp(extension,walk,3)) return 0;
70 return 1; /* default binary conversion */
71 default:
72 printk("Invalid conversion mode - defaulting to "
73 "binary.\n");
74 return 1;
75 }
76 }
77
78
79 /* File creation lock. This is system-wide to avoid deadlocks in rename. */
80 /* (rename might deadlock before detecting cross-FS moves.) */
81
82 static DECLARE_MUTEX(creation_lock);
83
84 void fat_lock_creation(void)
85 {
86 down(&creation_lock);
87 }
88
89
90 void fat_unlock_creation(void)
91 {
92 up(&creation_lock);
93 }
94
95
96 void lock_fat(struct super_block *sb)
97 {
98 down(&(MSDOS_SB(sb)->fat_lock));
99 }
100
101
102 void unlock_fat(struct super_block *sb)
103 {
104 up(&(MSDOS_SB(sb)->fat_lock));
105 }
106
107 /* Flushes the number of free clusters on FAT32 */
108 /* XXX: Need to write one per FSINFO block. Currently only writes 1 */
109 void fat_clusters_flush(struct super_block *sb)
110 {
111 int offset;
112 struct buffer_head *bh;
113 struct fat_boot_fsinfo *fsinfo;
114
115 /* The fat32 boot fs info is at offset 0x3e0 by observation */
116 offset = MSDOS_SB(sb)->fsinfo_offset;
117 bh = fat_bread(sb, (offset >> SECTOR_BITS));
118 if (bh == NULL) {
119 printk("FAT bread failed in fat_clusters_flush\n");
120 return;
121 }
122 fsinfo = (struct fat_boot_fsinfo *)
123 &bh->b_data[offset & (SECTOR_SIZE-1)];
124
125 /* Sanity check */
126 if (CF_LE_L(fsinfo->signature) != 0x61417272) {
127 printk("fat_clusters_flush: Did not find valid FSINFO signature. Found 0x%x. offset=0x%x\n", CF_LE_L(fsinfo->signature), offset);
128 fat_brelse(sb, bh);
129 return;
130 }
131 fsinfo->free_clusters = CF_LE_L(MSDOS_SB(sb)->free_clusters);
132 fat_mark_buffer_dirty(sb, bh);
133 fat_brelse(sb, bh);
134 }
135
136 /*
137 * fat_add_cluster tries to allocate a new cluster and adds it to the file
138 * represented by inode. The cluster is zero-initialized.
139 */
140
141 /* not a directory */
142
143 int fat_add_cluster(struct inode *inode)
144 {
145 struct super_block *sb = inode->i_sb;
146 int count,nr,limit,last,curr,file_cluster;
147 int res = -ENOSPC;
148 int cluster_size = MSDOS_SB(sb)->cluster_size;
149
150 if (!MSDOS_SB(sb)->free_clusters) return res;
151 lock_fat(sb);
152 limit = MSDOS_SB(sb)->clusters;
153 nr = limit; /* to keep GCC happy */
154 for (count = 0; count < limit; count++) {
155 nr = ((count+MSDOS_SB(sb)->prev_free) % limit)+2;
156 if (fat_access(sb,nr,-1) == 0) break;
157 }
158 MSDOS_SB(sb)->prev_free = (count+MSDOS_SB(sb)->prev_free+1) % limit;
159 if (count >= limit) {
160 MSDOS_SB(sb)->free_clusters = 0;
161 unlock_fat(sb);
162 return res;
163 }
164 fat_access(sb,nr,EOF_FAT(sb));
165 if (MSDOS_SB(sb)->free_clusters != -1)
166 MSDOS_SB(sb)->free_clusters--;
167 if (MSDOS_SB(sb)->fat_bits == 32)
168 fat_clusters_flush(sb);
169 unlock_fat(sb);
170 last = 0;
171 /* We must locate the last cluster of the file to add this
172 new one (nr) to the end of the link list (the FAT).
173
174 Here file_cluster will be the number of the last cluster of the
175 file (before we add nr).
176
177 last is the corresponding cluster number on the disk. We will
178 use last to plug the nr cluster. We will use file_cluster to
179 update the cache.
180 */
181 file_cluster = 0;
182 if ((curr = MSDOS_I(inode)->i_start) != 0) {
183 fat_cache_lookup(inode,INT_MAX,&last,&curr);
184 file_cluster = last;
185 while (curr && curr != -1){
186 file_cluster++;
187 if (!(curr = fat_access(sb, last = curr,-1))) {
188 fat_fs_panic(sb,"File without EOF");
189 return res;
190 }
191 }
192 }
193 if (last) fat_access(sb,last,nr);
194 else {
195 MSDOS_I(inode)->i_start = nr;
196 MSDOS_I(inode)->i_logstart = nr;
197 mark_inode_dirty(inode);
198 }
199 fat_cache_add(inode,file_cluster,nr);
200 inode->i_blocks += cluster_size;
201 return 0;
202 }
203
204 struct buffer_head *fat_extend_dir(struct inode *inode)
205 {
206 struct super_block *sb = inode->i_sb;
207 int count,nr,limit,last,curr,sector,last_sector,file_cluster;
208 struct buffer_head *bh, *res=NULL;
209 int cluster_size = MSDOS_SB(sb)->cluster_size;
210
211 if (MSDOS_SB(sb)->fat_bits != 32) {
212 if (inode->i_ino == MSDOS_ROOT_INO) return res;
213 }
214 if (!MSDOS_SB(sb)->free_clusters) return res;
215 lock_fat(sb);
216 limit = MSDOS_SB(sb)->clusters;
217 nr = limit; /* to keep GCC happy */
218 for (count = 0; count < limit; count++) {
219 nr = ((count+MSDOS_SB(sb)->prev_free) % limit)+2;
220 if (fat_access(sb,nr,-1) == 0) break;
221 }
222 PRINTK (("cnt = %d --",count));
223 #ifdef DEBUG
224 printk("free cluster: %d\n",nr);
225 #endif
226 MSDOS_SB(sb)->prev_free = (count+MSDOS_SB(sb)->prev_free+1) % limit;
227 if (count >= limit) {
228 MSDOS_SB(sb)->free_clusters = 0;
229 unlock_fat(sb);
230 return res;
231 }
232 fat_access(sb,nr,EOF_FAT(sb));
233 if (MSDOS_SB(sb)->free_clusters != -1)
234 MSDOS_SB(sb)->free_clusters--;
235 if (MSDOS_SB(sb)->fat_bits == 32)
236 fat_clusters_flush(sb);
237 unlock_fat(sb);
238 #ifdef DEBUG
239 printk("set to %x\n",fat_access(sb,nr,-1));
240 #endif
241 last = 0;
242 /* We must locate the last cluster of the file to add this
243 new one (nr) to the end of the link list (the FAT).
244
245 Here file_cluster will be the number of the last cluster of the
246 file (before we add nr).
247
248 last is the corresponding cluster number on the disk. We will
249 use last to plug the nr cluster. We will use file_cluster to
250 update the cache.
251 */
252 file_cluster = 0;
253 if ((curr = MSDOS_I(inode)->i_start) != 0) {
254 fat_cache_lookup(inode,INT_MAX,&last,&curr);
255 file_cluster = last;
256 while (curr && curr != -1){
257 PRINTK (("."));
258 file_cluster++;
259 if (!(curr = fat_access(sb,
260 last = curr,-1))) {
261 fat_fs_panic(sb,"File without EOF");
262 return res;
263 }
264 }
265 PRINTK ((" -- "));
266 }
267 #ifdef DEBUG
268 printk("last = %d\n",last);
269 #endif
270 if (last) fat_access(sb,last,nr);
271 else {
272 MSDOS_I(inode)->i_start = nr;
273 MSDOS_I(inode)->i_logstart = nr;
274 mark_inode_dirty(inode);
275 }
276 #ifdef DEBUG
277 if (last) printk("next set to %d\n",fat_access(sb,last,-1));
278 #endif
279 sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
280 last_sector = sector + cluster_size;
281 if (MSDOS_SB(sb)->cvf_format &&
282 MSDOS_SB(sb)->cvf_format->zero_out_cluster)
283 MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
284 else
285 for ( ; sector < last_sector; sector++) {
286 #ifdef DEBUG
287 printk("zeroing sector %d\n",sector);
288 #endif
289 if (!(bh = fat_getblk(sb, sector)))
290 printk("getblk failed\n");
291 else {
292 memset(bh->b_data,0,SECTOR_SIZE);
293 fat_set_uptodate(sb, bh, 1);
294 fat_mark_buffer_dirty(sb, bh);
295 if (!res)
296 res=bh;
297 else
298 fat_brelse(sb, bh);
299 }
300 }
301 if (file_cluster != inode->i_blocks/cluster_size){
302 printk ("file_cluster badly computed!!! %d <> %ld\n"
303 ,file_cluster,inode->i_blocks/cluster_size);
304 }else{
305 fat_cache_add(inode,file_cluster,nr);
306 }
307 inode->i_blocks += cluster_size;
308 if (inode->i_size & (SECTOR_SIZE-1)) {
309 fat_fs_panic(sb,"Odd directory size");
310 inode->i_size = (inode->i_size+SECTOR_SIZE) &
311 ~(SECTOR_SIZE-1);
312 }
313 inode->i_size += SECTOR_SIZE*cluster_size;
314 MSDOS_I(inode)->mmu_private += SECTOR_SIZE*cluster_size;
315 mark_inode_dirty(inode);
316 return res;
317 }
318
319 /* Linear day numbers of the respective 1sts in non-leap years. */
320
321 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
322 /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
323
324
325 extern struct timezone sys_tz;
326
327
328 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
329
330 int date_dos2unix(unsigned short time,unsigned short date)
331 {
332 int month,year,secs;
333
334 month = ((date >> 5) & 15)-1;
335 year = date >> 9;
336 secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
337 ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
338 month < 2 ? 1 : 0)+3653);
339 /* days since 1.1.70 plus 80's leap day */
340 secs += sys_tz.tz_minuteswest*60;
341 return secs;
342 }
343
344
345 /* Convert linear UNIX date to a MS-DOS time/date pair. */
346
347 void fat_date_unix2dos(int unix_date,unsigned short *time,
348 unsigned short *date)
349 {
350 int day,year,nl_day,month;
351
352 unix_date -= sys_tz.tz_minuteswest*60;
353 if (sys_tz.tz_dsttime) unix_date += 3600;
354
355 *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
356 (((unix_date/3600) % 24) << 11);
357 day = unix_date/86400-3652;
358 year = day/365;
359 if ((year+3)/4+365*year > day) year--;
360 day -= (year+3)/4+365*year;
361 if (day == 59 && !(year & 3)) {
362 nl_day = day;
363 month = 2;
364 }
365 else {
366 nl_day = (year & 3) || day <= 59 ? day : day-1;
367 for (month = 0; month < 12; month++)
368 if (day_n[month] > nl_day) break;
369 }
370 *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
371 }
372
373
374 /* Returns the inode number of the directory entry at offset pos. If bh is
375 non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
376 returned in bh.
377 AV. Most often we do it item-by-item. Makes sense to optimize.
378 AV. OK, there we go: if both bh and de are non-NULL we assume that we just
379 AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
380 AV. It's done in fat_get_entry() (inlined), here the slow case lives.
381 AV. Additionally, when we return -1 (i.e. reached the end of directory)
382 AV. we make bh NULL.
383 */
384
385 int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
386 struct msdos_dir_entry **de, int *ino)
387 {
388 struct super_block *sb = dir->i_sb;
389 int sector, offset;
390
391 while (1) {
392 offset = *pos;
393 PRINTK (("get_entry offset %d\n",offset));
394 if (*bh)
395 fat_brelse(sb, *bh);
396 *bh = NULL;
397 if ((sector = fat_bmap(dir,offset >> SECTOR_BITS)) == -1)
398 return -1;
399 PRINTK (("get_entry sector %d %p\n",sector,*bh));
400 PRINTK (("get_entry sector apres brelse\n"));
401 if (!sector)
402 return -1; /* beyond EOF */
403 *pos += sizeof(struct msdos_dir_entry);
404 if (!(*bh = fat_bread(sb, sector))) {
405 printk("Directory sread (sector 0x%x) failed\n",sector);
406 continue;
407 }
408 PRINTK (("get_entry apres sread\n"));
409 *de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
410 (SECTOR_SIZE-1)));
411 *ino = (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
412 MSDOS_DIR_BITS);
413 return 0;
414 }
415 }
416
417
418 /*
419 * Now an ugly part: this set of directory scan routines works on clusters
420 * rather than on inodes and sectors. They are necessary to locate the '..'
421 * directory "inode". raw_scan_sector operates in four modes:
422 *
423 * name number ino action
424 * -------- -------- -------- -------------------------------------------------
425 * non-NULL - X Find an entry with that name
426 * NULL non-NULL non-NULL Find an entry whose data starts at *number
427 * NULL non-NULL NULL Count subdirectories in *number. (*)
428 * NULL NULL non-NULL Find an empty entry
429 *
430 * (*) The return code should be ignored. It DOES NOT indicate success or
431 * failure. *number has to be initialized to zero.
432 *
433 * - = not used, X = a value is returned unless NULL
434 *
435 * If res_bh is non-NULL, the buffer is not deallocated but returned to the
436 * caller on success. res_de is set accordingly.
437 *
438 * If cont is non-zero, raw_found continues with the entry after the one
439 * res_bh/res_de point to.
440 */
441
442
443 #define RSS_NAME /* search for name */ \
444 done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
445 !(data[entry].attr & ATTR_VOLUME);
446
447 #define RSS_START /* search for start cluster */ \
448 done = !IS_FREE(data[entry].name) \
449 && ( \
450 ( \
451 (MSDOS_SB(sb)->fat_bits != 32) ? 0 : (CF_LE_W(data[entry].starthi) << 16) \
452 ) \
453 | CF_LE_W(data[entry].start) \
454 ) == *number;
455
456 #define RSS_FREE /* search for free entry */ \
457 { \
458 done = IS_FREE(data[entry].name); \
459 }
460
461 #define RSS_COUNT /* count subdirectories */ \
462 { \
463 done = 0; \
464 if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
465 (*number)++; \
466 }
467
468 static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
469 int *number,int *ino,struct buffer_head **res_bh,
470 struct msdos_dir_entry **res_de)
471 {
472 struct buffer_head *bh;
473 struct msdos_dir_entry *data;
474 int entry,start,done;
475
476 if (!(bh = fat_bread(sb,sector)))
477 return -EIO;
478 data = (struct msdos_dir_entry *) bh->b_data;
479 for (entry = 0; entry < MSDOS_DPS; entry++) {
480 /* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
481 if (name) {
482 RSS_NAME
483 } else {
484 if (!ino) RSS_COUNT
485 else {
486 if (number) RSS_START
487 else RSS_FREE
488 }
489 }
490 if (done) {
491 if (ino) *ino = sector*MSDOS_DPS+entry;
492 start = CF_LE_W(data[entry].start);
493 if (MSDOS_SB(sb)->fat_bits == 32) {
494 start |= (CF_LE_W(data[entry].starthi) << 16);
495 }
496 if (!res_bh)
497 fat_brelse(sb, bh);
498 else {
499 *res_bh = bh;
500 *res_de = &data[entry];
501 }
502 return start;
503 }
504 }
505 fat_brelse(sb, bh);
506 return -ENOENT;
507 }
508
509
510 /*
511 * raw_scan_root performs raw_scan_sector on the root directory until the
512 * requested entry is found or the end of the directory is reached.
513 */
514
515 static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
516 struct buffer_head **res_bh,struct msdos_dir_entry **res_de)
517 {
518 int count,cluster;
519
520 for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
521 if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
522 name,number,ino,res_bh,res_de)) >= 0) return cluster;
523 }
524 return -ENOENT;
525 }
526
527
528 /*
529 * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
530 * requested entry is found or the end of the directory is reached.
531 */
532
533 static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
534 int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
535 **res_de)
536 {
537 int count,cluster;
538
539 #ifdef DEBUG
540 printk("raw_scan_nonroot: start=%d\n",start);
541 #endif
542 do {
543 for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
544 if ((cluster = raw_scan_sector(sb,(start-2)*
545 MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
546 count,name,number,ino,res_bh,res_de)) >= 0)
547 return cluster;
548 }
549 if (!(start = fat_access(sb,start,-1))) {
550 fat_fs_panic(sb,"FAT error");
551 break;
552 }
553 #ifdef DEBUG
554 printk("next start: %d\n",start);
555 #endif
556 }
557 while (start != -1);
558 return -ENOENT;
559 }
560
561
562 /*
563 * raw_scan performs raw_scan_sector on any sector.
564 *
565 * NOTE: raw_scan must not be used on a directory that is is the process of
566 * being created.
567 */
568
569 static int raw_scan(struct super_block *sb, int start, const char *name,
570 int *number, int *ino, struct buffer_head **res_bh,
571 struct msdos_dir_entry **res_de)
572 {
573 if (start) return raw_scan_nonroot
574 (sb,start,name,number,ino,res_bh,res_de);
575 else return raw_scan_root
576 (sb,name,number,ino,res_bh,res_de);
577 }
578
579
580 /*
581 * fat_parent_ino returns the inode number of the parent directory of dir.
582 * File creation has to be deferred while fat_parent_ino is running to
583 * prevent renames.
584 *
585 * AV. Bad, bad, bad... We need a mapping that would give us inode by
586 * first cluster. Sheeeeit... OK, we can do it on fat_fill_inode() and
587 * update on fat_add_cluster(). When will we remove it? fat_clear_inode()
588 * and fat_truncate() to zero?
589 */
590
591 int fat_parent_ino(struct inode *dir,int locked)
592 {
593 static int zero = 0;
594 int error,curr,prev,nr;
595
596 PRINTK(("fat_parent_ino: Debug 0\n"));
597 if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
598 if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
599 if (!locked) fat_lock_creation(); /* prevent renames */
600 if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
601 &zero,NULL,NULL,NULL)) < 0) {
602 if (!locked) fat_unlock_creation();
603 return curr;
604 }
605 PRINTK(("fat_parent_ino: Debug 1 curr=%d\n", curr));
606 if (!curr) nr = MSDOS_ROOT_INO;
607 else {
608 PRINTK(("fat_parent_ino: Debug 2\n"));
609 if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
610 NULL,NULL)) < 0) {
611 PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev));
612 if (!locked) fat_unlock_creation();
613 return prev;
614 }
615 PRINTK(("fat_parent_ino: Debug 4 prev=%d\n", prev));
616 if (prev == 0 && MSDOS_SB(dir->i_sb)->fat_bits == 32) {
617 prev = MSDOS_SB(dir->i_sb)->root_cluster;
618 }
619 if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
620 NULL)) < 0) {
621 PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error));
622 if (!locked) fat_unlock_creation();
623 return error;
624 }
625 PRINTK(("fat_parent_ino: Debug 6 nr=%d\n", nr));
626 }
627 if (!locked) fat_unlock_creation();
628 return nr;
629 }
630
631
632 /*
633 * fat_subdirs counts the number of sub-directories of dir. It can be run
634 * on directories being created.
635 */
636
637 int fat_subdirs(struct inode *dir)
638 {
639 int count;
640
641 count = 0;
642 if ((dir->i_ino == MSDOS_ROOT_INO) &&
643 (MSDOS_SB(dir->i_sb)->fat_bits != 32)) {
644 (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL);
645 } else {
646 if ((dir->i_ino != MSDOS_ROOT_INO) &&
647 !MSDOS_I(dir)->i_start) return 0; /* in mkdir */
648 else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
649 NULL,&count,NULL,NULL,NULL);
650 }
651 return count;
652 }
653
654
655 /*
656 * Scans a directory for a given file (name points to its formatted name) or
657 * for an empty directory slot (name is NULL). Returns an error code or zero.
658 */
659
660 int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
661 struct msdos_dir_entry **res_de,int *ino)
662 {
663 int res;
664
665 res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
666 name, NULL, ino, res_bh, res_de);
667 return res<0 ? res : 0;
668 }
669
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.