xref: /illumos-gate/usr/src/cmd/fs.d/udfs/fsck/pass1.c (revision f45bbb21)
1 /*
2  * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #include <stdio.h>
29 #include <strings.h>
30 #include <malloc.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/mntent.h>
35 #include <sys/vnode.h>
36 #include <sys/fs/udf_volume.h>
37 #include <sys/dkio.h>
38 #include <sys/vtoc.h>
39 #include "fsck.h"
40 #include "udfs.h"
41 #include <locale.h>
42 
43 /*
44  * for each large file ( size > MAXOFF_T) this global counter
45  * gets incremented here.
46  */
47 
48 extern unsigned int largefile_count;
49 extern void	pwarn(char *, ...);
50 extern void	pfatal(char *, ...);
51 extern void	errexit(char *, ...);
52 
53 extern int32_t	verifytag(struct tag *, uint32_t, struct tag *, int);
54 extern char	*tagerrs[];
55 extern void	maketag(struct tag *, struct tag *);
56 extern void	flush(int32_t, struct bufarea *);
57 extern void	putfilentry(struct bufarea *);
58 extern int32_t	bread(int32_t, char *, daddr_t, long);
59 extern void	bwrite(int, char *, daddr_t, long);
60 extern int32_t	dofix(struct inodesc *, char *);
61 extern int32_t	reply(char *);
62 extern void	ud_swap_short_ad(short_ad_t *);
63 extern void	ud_swap_long_ad(long_ad_t *);
64 
65 extern void	dump16(char *, char *);
66 
67 static void	adjust(struct fileinfo *);
68 static void	opndir(struct file_entry *);
69 static int32_t	getdir(struct file_entry *, struct bufarea **,
70 	u_offset_t *, struct file_id **);
71 static void ckinode(struct file_entry *);
72 struct bufarea *getfilentry();
73 
74 /* Fields for traversing an allocation extent */
75 static uint32_t dir_adrsize;
76 static uint32_t dir_adrindx;
77 static uint32_t dir_naddrs;
78 static uint8_t *extbuf;
79 static uint8_t *dir_adrlist;
80 
81 /* Keep track of where we are in the directory */
82 static u_offset_t dir_baseoff;
83 static uint32_t dir_basesize;
84 static uint8_t *dirbuf;
85 static uint8_t *dir_fidp;
86 static uint32_t baseblock;
87 
88 #define	MAXFIDSIZE 2048
89 
90 static uint8_t fidbuf[MAXFIDSIZE];
91 
92 void
93 pass1()
94 {
95 	register struct file_entry *fp;
96 	register struct fileinfo *fip;
97 	register struct bufarea *bp;
98 	struct file_id *fidp;
99 	struct bufarea *fbp;
100 	int err;
101 
102 	(void) cachefile(rootblock, rootlen);
103 	fip = &inphead[0];		/* The root */
104 	fip->fe_lseen = 0;		/* Didn't get here through directory */
105 	n_files = n_dirs = 0;
106 	while (fip->fe_block) {
107 		u_offset_t offset, end;
108 
109 		markbusy(fip->fe_block, fip->fe_len);
110 		bp = getfilentry(fip->fe_block, fip->fe_len);
111 		if (bp == NULL) {
112 			pwarn(gettext("Unable to read file entry at %x\n"),
113 				fip->fe_block);
114 			goto next;
115 		}
116 		/* LINTED */
117 		fp = (struct file_entry *)bp->b_un.b_buf;
118 		fip->fe_lcount = fp->fe_lcount;
119 		fip->fe_type = fp->fe_icb_tag.itag_ftype;
120 		if (fp->fe_uniq_id >= maxuniqid)
121 			maxuniqid = fp->fe_uniq_id + 1;
122 
123 		if (fip->fe_block == rootblock &&
124 				fip->fe_type != FTYPE_DIRECTORY)
125 			errexit(gettext("Root file entry is not a "
126 				"directory\n"));
127 
128 		if (debug) {
129 			(void) printf("do %x len %d type %d lcount %d"
130 				" lseen %d end %llx\n",
131 				fip->fe_block, fip->fe_len,
132 				fip->fe_type, fip->fe_lcount,
133 				fip->fe_lseen, fp->fe_info_len);
134 		}
135 		switch (fip->fe_type) {
136 		case FTYPE_DIRECTORY:
137 			n_dirs++;
138 			offset = 0;
139 			end = fp->fe_info_len;
140 			fbp = NULL;
141 			opndir(fp);
142 			for (offset = 0; offset < end;
143 					offset += FID_LENGTH(fidp)) {
144 				err = getdir(fp, &fbp, &offset, &fidp);
145 				if (err) {
146 					pwarn(gettext("Bad directory entry in "
147 						"file %x at offset %llx\n"),
148 						fip->fe_block, offset);
149 					offset = end;
150 				}
151 				if (fidp->fid_flags & FID_DELETED)
152 					continue;
153 				(void) cachefile(fidp->fid_icb.lad_ext_loc,
154 					fidp->fid_icb.lad_ext_len);
155 			}
156 			if (dirbuf) {
157 				free(dirbuf);
158 				dirbuf = NULL;
159 			}
160 			if (fbp)
161 				fbp->b_flags &= ~B_INUSE;
162 			if (debug)
163 				(void) printf("Done %x\n", fip->fe_block);
164 			break;
165 
166 		case FTYPE_FILE:
167 		case FTYPE_SYMLINK:
168 			ckinode(fp);
169 			/* FALLTHROUGH */
170 		default:
171 			n_files++;
172 			break;
173 		}
174 		putfilentry(bp);
175 		bp->b_flags &= ~B_INUSE;
176 	next:
177 		/* At end of this set of fips, get the next set */
178 		if ((++fip)->fe_block == (uint32_t)-1)
179 			fip = fip->fe_nexthash;
180 	}
181 
182 	/* Find bad link counts */
183 	fip = &inphead[0];
184 	while (fip->fe_block) {
185 		if (fip->fe_lcount != fip->fe_lseen)
186 			adjust(fip);
187 		/* At end of this set of fips, get the next set */
188 		if ((++fip)->fe_block == (uint32_t)-1)
189 			fip = fip->fe_nexthash;
190 	}
191 }
192 
193 static void
194 opndir(struct file_entry *fp)
195 {
196 	if (dirbuf) {
197 		free(dirbuf);
198 		dirbuf = NULL;
199 	}
200 	if (extbuf) {
201 		free(extbuf);
202 		extbuf = NULL;
203 	}
204 
205 	dir_baseoff = 0;
206 	dir_basesize = 0;
207 	dir_adrindx = 0;
208 
209 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
210 	case ICB_FLAG_SHORT_AD:
211 		dir_adrsize = sizeof (short_ad_t);
212 		dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t);
213 		dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
214 		break;
215 	case ICB_FLAG_LONG_AD:
216 		dir_adrsize = sizeof (long_ad_t);
217 		dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t);
218 		dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
219 		break;
220 	case ICB_FLAG_EXT_AD:
221 		errexit(gettext("Can't handle ext_ads in directories/n"));
222 		break;
223 	case ICB_FLAG_ONE_AD:
224 		dir_adrsize = 0;
225 		dir_naddrs = 0;
226 		dir_adrlist = NULL;
227 		dir_basesize = fp->fe_len_adesc;
228 		dir_fidp = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
229 		baseblock = fp->fe_tag.tag_loc;
230 		break;
231 	}
232 }
233 
234 /* Allocate and read in an allocation extent */
235 /* ARGSUSED */
236 int
237 getallocext(struct file_entry *fp, uint32_t loc, uint32_t len)
238 {
239 	uint32_t nb;
240 	uint8_t *ap;
241 	int i;
242 	int err;
243 	struct alloc_ext_desc *aep;
244 
245 	if (debug)
246 		(void) printf(" allocext loc %x len %x\n", loc, len);
247 	nb = roundup(len, secsize);
248 	if (extbuf)
249 		free(extbuf);
250 	extbuf = (uint8_t *)malloc(nb);
251 	if (extbuf == NULL)
252 		errexit(gettext("Can't allocate directory extent buffer\n"));
253 	if (bread(fsreadfd, (char *)extbuf,
254 			fsbtodb(loc + part_start), nb) != 0) {
255 		(void) fprintf(stderr,
256 			gettext("Can't read allocation extent\n"));
257 		return (1);
258 	}
259 	/* LINTED */
260 	aep = (struct alloc_ext_desc *)extbuf;
261 	err = verifytag(&aep->aed_tag, loc, &aep->aed_tag, UD_ALLOC_EXT_DESC);
262 	if (err) {
263 		(void) printf(
264 			gettext("Bad tag on alloc extent: %s\n"), tagerrs[err]);
265 		free(extbuf);
266 		return (1);
267 	}
268 	dir_adrlist = (uint8_t *)(aep + 1);
269 	dir_naddrs = aep->aed_len_aed / dir_adrsize;
270 	dir_adrindx = 0;
271 
272 	/* Swap the descriptors */
273 	for (i = 0, ap = dir_adrlist; i < dir_naddrs; i++, ap += dir_adrsize) {
274 		if (dir_adrsize == sizeof (short_ad_t)) {
275 			/* LINTED */
276 			ud_swap_short_ad((short_ad_t *)ap);
277 		} else if (dir_adrsize == sizeof (long_ad_t)) {
278 			/* LINTED */
279 			ud_swap_long_ad((long_ad_t *)ap);
280 		}
281 	}
282 
283 	return (0);
284 }
285 
286 /*
287  * Variables used in this function and their relationships:
288  *  *poffset - read pointer in the directory
289  *  dir_baseoff - offset at start of dirbuf
290  *  dir_baselen - length of valid data in current extent
291  *  dir_adrindx - index into current allocation extent for location of
292  *	dir_baseoff
293  *  dir_naddrs - number of entries in current allocation extent
294  *  dir_fidp - pointer to dirbuf or immediate data in file entry
295  *  baseblock - block address of dir_baseoff
296  *  newoff - *poffset - dir_baseoff
297  */
298 /* ARGSUSED1 */
299 static int32_t
300 getdir(struct file_entry *fp, struct bufarea **fbp,
301 	u_offset_t *poffset, struct file_id **fidpp)
302 {
303 	/* LINTED */
304 	register struct file_id *fidp = (struct file_id *)fidbuf;
305 	register struct short_ad *sap;
306 	register struct long_ad *lap;
307 	register int i, newoff, xoff = 0;
308 	uint32_t block = 0, nb, len, left;
309 	u_offset_t offset;
310 	int err, type;
311 
312 
313 again:
314 	offset = *poffset;
315 again2:
316 	if (debug)
317 		(void) printf("getdir %llx\n", offset);
318 	newoff = offset - dir_baseoff;
319 	if (newoff >= dir_basesize) {
320 		if (dirbuf) {
321 			free(dirbuf);
322 			dirbuf = NULL;
323 		}
324 	} else {
325 		if (block == 0)
326 			block = baseblock + (newoff / secsize);
327 		goto nextone;
328 	}
329 
330 again3:
331 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
332 	case ICB_FLAG_SHORT_AD:
333 		/* LINTED */
334 		sap = &((short_ad_t *)dir_adrlist)[dir_adrindx];
335 		for (i = dir_adrindx; i < dir_naddrs; i++, sap++) {
336 			len = EXTLEN(sap->sad_ext_len);
337 			type = EXTYPE(sap->sad_ext_len);
338 			if (type == 3) {
339 				if (i < dir_naddrs - 1)
340 					errexit(gettext("Allocation extent not "
341 						"at end of list\n"));
342 				markbusy(sap->sad_ext_loc, len);
343 				if (getallocext(fp, sap->sad_ext_loc, len))
344 					return (1);
345 				goto again3;
346 			}
347 			if (newoff < len)
348 				break;
349 			newoff -= len;
350 			dir_baseoff += len;
351 			if (debug)
352 				(void) printf(
353 				    " loc %x len %x\n", sap->sad_ext_loc,
354 					len);
355 		}
356 		dir_adrindx = i;
357 		if (debug)
358 			(void) printf(" loc %x len %x\n", sap->sad_ext_loc,
359 				sap->sad_ext_len);
360 		baseblock = sap->sad_ext_loc;
361 		if (block == 0)
362 			block = baseblock;
363 		dir_basesize = len;
364 		if (type < 2)
365 			markbusy(sap->sad_ext_loc, len);
366 		if (type != 0) {
367 			*poffset += dir_basesize;
368 			goto again;
369 		}
370 		nb = roundup(len, secsize);
371 		dirbuf = (uint8_t *)malloc(nb);
372 		if (dirbuf == NULL)
373 			errexit(gettext("Can't allocate directory extent "
374 				"buffer\n"));
375 		if (bread(fsreadfd, (char *)dirbuf,
376 				fsbtodb(baseblock + part_start), nb) != 0) {
377 			errexit(gettext("Can't read directory extent\n"));
378 		}
379 		dir_fidp = dirbuf;
380 		break;
381 	case ICB_FLAG_LONG_AD:
382 		/* LINTED */
383 		lap = &((long_ad_t *)dir_adrlist)[dir_adrindx];
384 		for (i = dir_adrindx; i < dir_naddrs; i++, lap++) {
385 			len = EXTLEN(lap->lad_ext_len);
386 			type = EXTYPE(lap->lad_ext_len);
387 			if (type == 3) {
388 				if (i < dir_naddrs - 1)
389 					errexit(gettext("Allocation extent not "
390 						"at end of list\n"));
391 				markbusy(lap->lad_ext_loc, len);
392 				if (getallocext(fp, lap->lad_ext_loc, len))
393 					return (1);
394 				goto again3;
395 			}
396 			if (newoff < len)
397 				break;
398 			newoff -= len;
399 			dir_baseoff += len;
400 			if (debug)
401 				(void) printf(
402 				    " loc %x len %x\n", lap->lad_ext_loc,
403 					len);
404 		}
405 		dir_adrindx = i;
406 		if (debug)
407 			(void) printf(" loc %x len %x\n", lap->lad_ext_loc,
408 				lap->lad_ext_len);
409 		baseblock = lap->lad_ext_loc;
410 		if (block == 0)
411 			block = baseblock;
412 		dir_basesize = len;
413 		if (type < 2)
414 			markbusy(lap->lad_ext_loc, len);
415 		if (type != 0) {
416 			*poffset += dir_basesize;
417 			goto again;
418 		}
419 		nb = roundup(len, secsize);
420 		dirbuf = (uint8_t *)malloc(nb);
421 		if (dirbuf == NULL)
422 			errexit(gettext("Can't allocate directory extent "
423 				"buffer\n"));
424 		if (bread(fsreadfd, (char *)dirbuf,
425 				fsbtodb(baseblock + part_start), nb) != 0) {
426 			errexit(gettext("Can't read directory extent\n"));
427 		}
428 		dir_fidp = dirbuf;
429 		break;
430 	case ICB_FLAG_EXT_AD:
431 		break;
432 	case ICB_FLAG_ONE_AD:
433 		errexit(gettext("Logic error in getdir - at ICB_FLAG_ONE_AD "
434 			"case\n"));
435 		break;
436 	}
437 nextone:
438 	if (debug)
439 		(void) printf("getdirend blk %x dir_baseoff %llx newoff %x\n",
440 			block, dir_baseoff, newoff);
441 	left = dir_basesize - newoff;
442 	if (xoff + left > MAXFIDSIZE)
443 		left = MAXFIDSIZE - xoff;
444 	bcopy((char *)dir_fidp + newoff, (char *)fidbuf + xoff, left);
445 	xoff += left;
446 	/*
447 	 * If we have a fid that crosses an extent boundary, then force
448 	 * a read of the next extent, and fill up the rest of the fid.
449 	 */
450 	if (xoff < sizeof (fidp->fid_tag) ||
451 	    xoff < sizeof (fidp->fid_tag) + SWAP16(fidp->fid_tag.tag_crc_len)) {
452 		offset += left;
453 		if (debug)
454 			(void) printf("block crossing at offset %llx\n",
455 				offset);
456 		goto again2;
457 	}
458 	err = verifytag(&fidp->fid_tag, block, &fidp->fid_tag, UD_FILE_ID_DESC);
459 	if (debug) {
460 		dump16((char *)fidp, "\n");
461 	}
462 	if (err) {
463 		pwarn(gettext("Bad directory tag: %s\n"), tagerrs[err]);
464 		return (err);
465 	}
466 	*fidpp = fidp;
467 	return (0);
468 }
469 
470 static void
471 ckinode(struct file_entry *fp)
472 {
473 	register struct short_ad *sap;
474 	register struct long_ad *lap;
475 	register int i, type, len;
476 
477 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
478 	case ICB_FLAG_SHORT_AD:
479 		dir_adrsize = sizeof (short_ad_t);
480 		dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t);
481 		/* LINTED */
482 		sap = (short_ad_t *)(fp->fe_spec + fp->fe_len_ear);
483 again1:
484 		for (i = 0; i < dir_naddrs; i++, sap++) {
485 			len = EXTLEN(sap->sad_ext_len);
486 			type = EXTYPE(sap->sad_ext_len);
487 			if (type < 2)
488 				markbusy(sap->sad_ext_loc, len);
489 			if (debug)
490 				(void) printf(
491 				    " loc %x len %x\n", sap->sad_ext_loc,
492 					sap->sad_ext_len);
493 			if (type == 3) {
494 				markbusy(sap->sad_ext_loc, len);
495 				/* This changes dir_naddrs and dir_adrlist */
496 				if (getallocext(fp, sap->sad_ext_loc, len))
497 					break;
498 				/* LINTED */
499 				sap = (short_ad_t *)dir_adrlist;
500 				goto again1;
501 			}
502 		}
503 		break;
504 	case ICB_FLAG_LONG_AD:
505 		dir_adrsize = sizeof (long_ad_t);
506 		dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t);
507 		/* LINTED */
508 		lap = (long_ad_t *)(fp->fe_spec + fp->fe_len_ear);
509 again2:
510 		for (i = 0; i < dir_naddrs; i++, lap++) {
511 			len = EXTLEN(lap->lad_ext_len);
512 			type = EXTYPE(lap->lad_ext_len);
513 			if (type < 2)
514 				markbusy(lap->lad_ext_loc, len);
515 			if (debug)
516 				(void) printf(
517 				    " loc %x len %x\n", lap->lad_ext_loc,
518 					lap->lad_ext_len);
519 			if (type == 3) {
520 				markbusy(sap->sad_ext_loc, len);
521 				/* This changes dir_naddrs and dir_adrlist */
522 				if (getallocext(fp, lap->lad_ext_loc, len))
523 					break;
524 				/* LINTED */
525 				lap = (long_ad_t *)dir_adrlist;
526 				goto again2;
527 			}
528 		}
529 		break;
530 	case ICB_FLAG_EXT_AD:
531 		break;
532 	case ICB_FLAG_ONE_AD:
533 		break;
534 	}
535 }
536 
537 static void
538 adjust(struct fileinfo *fip)
539 {
540 	register struct file_entry *fp;
541 	register struct bufarea *bp;
542 
543 	bp = getfilentry(fip->fe_block, fip->fe_len);
544 	if (bp == NULL)
545 		errexit(gettext("Unable to read file entry at %x\n"),
546 			fip->fe_block);
547 	/* LINTED */
548 	fp = (struct file_entry *)bp->b_un.b_buf;
549 	pwarn(gettext("LINK COUNT %s I=%x"),
550 		fip->fe_type == FTYPE_DIRECTORY ? "DIR" :
551 		fip->fe_type == FTYPE_SYMLINK ? "SYM" :
552 		fip->fe_type == FTYPE_FILE ? "FILE" : "???", fip->fe_block);
553 	(void) printf(gettext(" COUNT %d SHOULD BE %d"),
554 		fip->fe_lcount, fip->fe_lseen);
555 	if (preen) {
556 		if (fip->fe_lseen > fip->fe_lcount) {
557 			(void) printf("\n");
558 			pfatal(gettext("LINK COUNT INCREASING"));
559 		}
560 		(void) printf(gettext(" (ADJUSTED)\n"));
561 	}
562 	if (preen || reply(gettext("ADJUST")) == 1) {
563 		fp->fe_lcount = fip->fe_lseen;
564 		putfilentry(bp);
565 		dirty(bp);
566 		flush(fswritefd, bp);
567 	}
568 	bp->b_flags &= ~B_INUSE;
569 }
570 
571 void
572 dofreemap()
573 {
574 	register int i;
575 	register char *bp, *fp;
576 	struct inodesc idesc;
577 
578 	if (freemap == NULL)
579 		return;
580 
581 	/* Flip bits in the busy map */
582 	bp = busymap;
583 	for (i = 0, bp = busymap; i < part_bmp_bytes; i++, bp++)
584 		*bp = ~*bp;
585 
586 	/* Mark leftovers in byte as allocated */
587 	if (part_len % NBBY)
588 		bp[-1] &= (unsigned)0xff >> (NBBY - part_len % NBBY);
589 	bp = busymap;
590 	fp = freemap;
591 	bzero((char *)&idesc, sizeof (struct inodesc));
592 	idesc.id_type = ADDR;
593 	if (bcmp(bp, fp, part_bmp_bytes) != 0 &&
594 		dofix(&idesc, gettext("BLK(S) MISSING IN FREE BITMAP"))) {
595 		bcopy(bp, fp, part_bmp_bytes);
596 		maketag(&spacep->sbd_tag, &spacep->sbd_tag);
597 		bwrite(fswritefd, (char *)spacep, fsbtodb(part_bmp_loc),
598 			part_bmp_sectors * secsize);
599 	}
600 }
601 
602 void
603 dolvint()
604 {
605 	struct lvid_iu *lviup;
606 	struct inodesc idesc;
607 
608 	bzero((char *)&idesc, sizeof (struct inodesc));
609 	idesc.id_type = ADDR;
610 	lviup = (struct lvid_iu *)&lvintp->lvid_fst[2];
611 	if ((lvintp->lvid_fst[0] != part_len - n_blks ||
612 	    lvintp->lvid_int_type != LVI_CLOSE ||
613 	    lviup->lvidiu_nfiles != n_files ||
614 	    lviup->lvidiu_ndirs != n_dirs ||
615 	    lvintp->lvid_uniqid < maxuniqid) &&
616 	    dofix(&idesc, gettext("LOGICAL VOLUME INTEGRITY COUNTS WRONG"))) {
617 		lvintp->lvid_int_type = LVI_CLOSE;
618 		lvintp->lvid_fst[0] = part_len - n_blks;
619 		lviup->lvidiu_nfiles = n_files;
620 		lviup->lvidiu_ndirs = n_dirs;
621 		lvintp->lvid_uniqid = maxuniqid;
622 		maketag(&lvintp->lvid_tag, &lvintp->lvid_tag);
623 		bwrite(fswritefd, (char *)lvintp, fsbtodb(lvintblock),
624 			lvintlen);
625 	}
626 }
627