rock.c 14.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 *  linux/fs/isofs/rock.c
 *
 *  (C) 1992, 1993  Eric Youngdale
 *
 *  Rock Ridge Extensions to iso9660
 */

#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>

Al Viro's avatar
Al Viro committed
13
#include "isofs.h"
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16 17 18 19 20 21 22
#include "rock.h"

/* These functions are designed to read the system areas of a directory record
 * and extract relevant information.  There are different functions provided
 * depending upon what information we need at the time.  One function fills
 * out an inode structure, a second one extracts a filename, a third one
 * returns a symbolic link name, and a fourth one returns the extent number
 * for the file. */

Andrew Morton's avatar
Andrew Morton committed
23
#define SIG(A,B) ((A) | ((B) << 8))	/* isonum_721() */
Linus Torvalds's avatar
Linus Torvalds committed
24

Andrew Morton's avatar
Andrew Morton committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*
 * This is a way of ensuring that we have something in the system
 *  use fields that is compatible with Rock Ridge.  Return zero on success.
 */

static int check_sp(struct rock_ridge *rr, struct inode *inode)
{
	if (rr->u.SP.magic[0] != 0xbe)
		return -1;
	if (rr->u.SP.magic[1] != 0xef)
		return -1;
	ISOFS_SB(inode->i_sb)->s_rock_offset = rr->u.SP.skip;
	return 0;
}

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
static void setup_rock_ridge(struct iso_directory_record *de,
			struct inode *inode, unsigned char **chr, int *len)
{
	*len = sizeof(struct iso_directory_record) + de->name_len[0];
	if (*len & 1)
		(*len)++;
	*chr = (unsigned char *)de + *len;
	*len = *((unsigned char *)de) - *len;
	if (*len < 0)
		*len = 0;

	if (ISOFS_SB(inode->i_sb)->s_rock_offset != -1) {
		*len -= ISOFS_SB(inode->i_sb)->s_rock_offset;
		*chr += ISOFS_SB(inode->i_sb)->s_rock_offset;
		if (*len < 0)
			*len = 0;
	}
Andrew Morton's avatar
Andrew Morton committed
57
}
Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

#define MAYBE_CONTINUE(LABEL,DEV) \
  {if (buffer) { kfree(buffer); buffer = NULL; } \
  if (cont_extent){ \
    int block, offset, offset1; \
    struct buffer_head * pbh; \
    buffer = kmalloc(cont_size,GFP_KERNEL); \
    if (!buffer) goto out; \
    block = cont_extent; \
    offset = cont_offset; \
    offset1 = 0; \
    pbh = sb_bread(DEV->i_sb, block); \
    if(pbh){       \
      if (offset > pbh->b_size || offset + cont_size > pbh->b_size){	\
	brelse(pbh); \
	goto out; \
      } \
      memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
      brelse(pbh); \
      chr = (unsigned char *) buffer; \
      len = cont_size; \
      cont_extent = 0; \
      cont_size = 0; \
      cont_offset = 0; \
      goto LABEL; \
    }    \
    printk("Unable to read rock-ridge attributes\n");    \
  }}

/* return length of name field; 0: not found, -1: to be ignored */
Andrew Morton's avatar
Andrew Morton committed
88 89
int get_rock_ridge_filename(struct iso_directory_record *de,
			    char *retname, struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
90
{
Andrew Morton's avatar
Andrew Morton committed
91 92
	int len;
	unsigned char *chr;
93 94 95 96
	int cont_extent = 0;
	int cont_offset = 0;
	int cont_size = 0;
	void *buffer = NULL;
Andrew Morton's avatar
Andrew Morton committed
97 98 99 100
	struct rock_ridge *rr;
	int sig;
	int retnamlen = 0;
	int truncate = 0;
Linus Torvalds's avatar
Linus Torvalds committed
101

Andrew Morton's avatar
Andrew Morton committed
102 103 104 105
	if (!ISOFS_SB(inode->i_sb)->s_rock)
		return 0;
	*retname = 0;

106
	setup_rock_ridge(de, inode, &chr, &len);
Andrew Morton's avatar
Andrew Morton committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
repeat:

	while (len > 2) { /* There may be one byte for padding somewhere */
		rr = (struct rock_ridge *)chr;
		if (rr->len < 3)
			goto out;	/* Something got screwed up here */
		sig = isonum_721(chr);
		chr += rr->len;
		len -= rr->len;
		if (len < 0)
			goto out;	/* corrupted isofs */

		switch (sig) {
		case SIG('R', 'R'):
			if ((rr->u.RR.flags[0] & RR_NM) == 0)
				goto out;
			break;
		case SIG('S', 'P'):
Andrew Morton's avatar
Andrew Morton committed
125 126
			if (check_sp(rr, inode))
				goto out;
Andrew Morton's avatar
Andrew Morton committed
127 128
			break;
		case SIG('C', 'E'):
Andrew Morton's avatar
Andrew Morton committed
129 130 131
			cont_extent = isonum_733(rr->u.CE.extent);
			cont_offset = isonum_733(rr->u.CE.offset);
			cont_size = isonum_733(rr->u.CE.size);
Andrew Morton's avatar
Andrew Morton committed
132 133 134
			break;
		case SIG('N', 'M'):
			if (truncate)
Andrew Morton's avatar
Andrew Morton committed
135
				break;
Andrew Morton's avatar
Andrew Morton committed
136
			if (rr->len < 5)
Andrew Morton's avatar
Andrew Morton committed
137
				break;
Andrew Morton's avatar
Andrew Morton committed
138 139 140 141 142 143 144 145
			/*
			 * If the flags are 2 or 4, this indicates '.' or '..'.
			 * We don't want to do anything with this, because it
			 * screws up the code that calls us.  We don't really
			 * care anyways, since we can just use the non-RR
			 * name.
			 */
			if (rr->u.NM.flags & 6)
Andrew Morton's avatar
Andrew Morton committed
146 147
				break;

Andrew Morton's avatar
Andrew Morton committed
148 149 150
			if (rr->u.NM.flags & ~1) {
				printk("Unsupported NM flag settings (%d)\n",
					rr->u.NM.flags);
Andrew Morton's avatar
Andrew Morton committed
151
				break;
Andrew Morton's avatar
Andrew Morton committed
152 153 154
			}
			if ((strlen(retname) + rr->len - 5) >= 254) {
				truncate = 1;
Andrew Morton's avatar
Andrew Morton committed
155 156
				break;
			}
Andrew Morton's avatar
Andrew Morton committed
157 158 159 160 161 162 163 164 165
			strncat(retname, rr->u.NM.name, rr->len - 5);
			retnamlen += rr->len - 5;
			break;
		case SIG('R', 'E'):
			if (buffer)
				kfree(buffer);
			return -1;
		default:
			break;
Andrew Morton's avatar
Andrew Morton committed
166
		}
Linus Torvalds's avatar
Linus Torvalds committed
167
	}
Andrew Morton's avatar
Andrew Morton committed
168
	MAYBE_CONTINUE(repeat, inode);
Andrew Morton's avatar
Andrew Morton committed
169
	kfree(buffer);
Andrew Morton's avatar
Andrew Morton committed
170
	return retnamlen;	/* If 0, this file did not have a NM field */
Andrew Morton's avatar
Andrew Morton committed
171
out:
Andrew Morton's avatar
Andrew Morton committed
172 173 174
	if (buffer)
		kfree(buffer);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
175 176 177 178 179 180
}

static int
parse_rock_ridge_inode_internal(struct iso_directory_record *de,
				struct inode *inode, int regard_xa)
{
Andrew Morton's avatar
Andrew Morton committed
181 182 183
	int len;
	unsigned char *chr;
	int symlink_len = 0;
Andrew Morton's avatar
Andrew Morton committed
184 185 186 187
	int cnt, sig;
	struct inode *reloc;
	struct rock_ridge *rr;
	int rootflag;
188 189 190 191
	int cont_extent = 0;
	int cont_offset = 0;
	int cont_size = 0;
	void *buffer = NULL;
Andrew Morton's avatar
Andrew Morton committed
192 193 194 195

	if (!ISOFS_SB(inode->i_sb)->s_rock)
		return 0;

196
	setup_rock_ridge(de, inode, &chr, &len);
Andrew Morton's avatar
Andrew Morton committed
197 198 199 200 201 202 203
	if (regard_xa) {
		chr += 14;
		len -= 14;
		if (len < 0)
			len = 0;
	}

Andrew Morton's avatar
Andrew Morton committed
204 205 206 207 208 209 210 211 212 213 214 215
repeat:
	while (len > 2) { /* There may be one byte for padding somewhere */
		rr = (struct rock_ridge *)chr;
		if (rr->len < 3)
			goto out;	/* Something got screwed up here */
		sig = isonum_721(chr);
		chr += rr->len;
		len -= rr->len;
		if (len < 0)
			goto out;	/* corrupted isofs */

		switch (sig) {
Linus Torvalds's avatar
Linus Torvalds committed
216
#ifndef CONFIG_ZISOFS		/* No flag for SF or ZF */
Andrew Morton's avatar
Andrew Morton committed
217 218 219 220 221
		case SIG('R', 'R'):
			if ((rr->u.RR.flags[0] &
			     (RR_PX | RR_TF | RR_SL | RR_CL)) == 0)
				goto out;
			break;
Linus Torvalds's avatar
Linus Torvalds committed
222
#endif
Andrew Morton's avatar
Andrew Morton committed
223
		case SIG('S', 'P'):
Andrew Morton's avatar
Andrew Morton committed
224 225
			if (check_sp(rr, inode))
				goto out;
Andrew Morton's avatar
Andrew Morton committed
226 227
			break;
		case SIG('C', 'E'):
Andrew Morton's avatar
Andrew Morton committed
228 229 230
			cont_extent = isonum_733(rr->u.CE.extent);
			cont_offset = isonum_733(rr->u.CE.offset);
			cont_size = isonum_733(rr->u.CE.size);
Andrew Morton's avatar
Andrew Morton committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
			break;
		case SIG('E', 'R'):
			ISOFS_SB(inode->i_sb)->s_rock = 1;
			printk(KERN_DEBUG "ISO 9660 Extensions: ");
			{
				int p;
				for (p = 0; p < rr->u.ER.len_id; p++)
					printk("%c", rr->u.ER.data[p]);
			}
			printk("\n");
			break;
		case SIG('P', 'X'):
			inode->i_mode = isonum_733(rr->u.PX.mode);
			inode->i_nlink = isonum_733(rr->u.PX.n_links);
			inode->i_uid = isonum_733(rr->u.PX.uid);
			inode->i_gid = isonum_733(rr->u.PX.gid);
			break;
		case SIG('P', 'N'):
			{
				int high, low;
				high = isonum_733(rr->u.PN.dev_high);
				low = isonum_733(rr->u.PN.dev_low);
				/*
				 * The Rock Ridge standard specifies that if
				 * sizeof(dev_t) <= 4, then the high field is
				 * unused, and the device number is completely
				 * stored in the low field.  Some writers may
				 * ignore this subtlety,
				 * and as a result we test to see if the entire
				 * device number is
				 * stored in the low field, and use that.
				 */
				if ((low & ~0xff) && high == 0) {
					inode->i_rdev =
					    MKDEV(low >> 8, low & 0xff);
				} else {
					inode->i_rdev =
					    MKDEV(high, low);
Andrew Morton's avatar
Andrew Morton committed
269
				}
Andrew Morton's avatar
Andrew Morton committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
			}
			break;
		case SIG('T', 'F'):
			/*
			 * Some RRIP writers incorrectly place ctime in the
			 * TF_CREATE field. Try to handle this correctly for
			 * either case.
			 */
			/* Rock ridge never appears on a High Sierra disk */
			cnt = 0;
			if (rr->u.TF.flags & TF_CREATE) {
				inode->i_ctime.tv_sec =
				    iso_date(rr->u.TF.times[cnt++].time,
					     0);
				inode->i_ctime.tv_nsec = 0;
			}
			if (rr->u.TF.flags & TF_MODIFY) {
				inode->i_mtime.tv_sec =
				    iso_date(rr->u.TF.times[cnt++].time,
					     0);
				inode->i_mtime.tv_nsec = 0;
			}
			if (rr->u.TF.flags & TF_ACCESS) {
				inode->i_atime.tv_sec =
				    iso_date(rr->u.TF.times[cnt++].time,
					     0);
				inode->i_atime.tv_nsec = 0;
			}
			if (rr->u.TF.flags & TF_ATTRIBUTES) {
				inode->i_ctime.tv_sec =
				    iso_date(rr->u.TF.times[cnt++].time,
					     0);
				inode->i_ctime.tv_nsec = 0;
			}
			break;
		case SIG('S', 'L'):
			{
				int slen;
				struct SL_component *slp;
				struct SL_component *oldslp;
				slen = rr->len - 5;
				slp = &rr->u.SL.link;
				inode->i_size = symlink_len;
				while (slen > 1) {
					rootflag = 0;
					switch (slp->flags & ~1) {
					case 0:
						inode->i_size +=
						    slp->len;
						break;
					case 2:
						inode->i_size += 1;
						break;
					case 4:
						inode->i_size += 2;
						break;
					case 8:
						rootflag = 1;
						inode->i_size += 1;
						break;
					default:
						printk("Symlink component flag "
							"not implemented\n");
Andrew Morton's avatar
Andrew Morton committed
333
					}
Andrew Morton's avatar
Andrew Morton committed
334 335 336 337 338 339 340 341 342 343 344
					slen -= slp->len + 2;
					oldslp = slp;
					slp = (struct SL_component *)
						(((char *)slp) + slp->len + 2);

					if (slen < 2) {
						if (((rr->u.SL.
						      flags & 1) != 0)
						    &&
						    ((oldslp->
						      flags & 1) == 0))
Andrew Morton's avatar
Andrew Morton committed
345
							inode->i_size +=
Andrew Morton's avatar
Andrew Morton committed
346 347
							    1;
						break;
Andrew Morton's avatar
Andrew Morton committed
348
					}
Andrew Morton's avatar
Andrew Morton committed
349 350 351 352 353 354 355 356

					/*
					 * If this component record isn't
					 * continued, then append a '/'.
					 */
					if (!rootflag
					    && (oldslp->flags & 1) == 0)
						inode->i_size += 1;
Andrew Morton's avatar
Andrew Morton committed
357
				}
Andrew Morton's avatar
Andrew Morton committed
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
			}
			symlink_len = inode->i_size;
			break;
		case SIG('R', 'E'):
			printk(KERN_WARNING "Attempt to read inode for "
					"relocated directory\n");
			goto out;
		case SIG('C', 'L'):
			ISOFS_I(inode)->i_first_extent =
			    isonum_733(rr->u.CL.location);
			reloc =
			    isofs_iget(inode->i_sb,
				       ISOFS_I(inode)->i_first_extent,
				       0);
			if (!reloc)
Andrew Morton's avatar
Andrew Morton committed
373
				goto out;
Andrew Morton's avatar
Andrew Morton committed
374 375 376 377 378 379 380 381 382 383 384 385
			inode->i_mode = reloc->i_mode;
			inode->i_nlink = reloc->i_nlink;
			inode->i_uid = reloc->i_uid;
			inode->i_gid = reloc->i_gid;
			inode->i_rdev = reloc->i_rdev;
			inode->i_size = reloc->i_size;
			inode->i_blocks = reloc->i_blocks;
			inode->i_atime = reloc->i_atime;
			inode->i_ctime = reloc->i_ctime;
			inode->i_mtime = reloc->i_mtime;
			iput(reloc);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
386
#ifdef CONFIG_ZISOFS
Andrew Morton's avatar
Andrew Morton committed
387 388 389 390
		case SIG('Z', 'F'): {
			int algo;

			if (ISOFS_SB(inode->i_sb)->s_nocompress)
Andrew Morton's avatar
Andrew Morton committed
391
				break;
Andrew Morton's avatar
Andrew Morton committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
			algo = isonum_721(rr->u.ZF.algorithm);
			if (algo == SIG('p', 'z')) {
				int block_shift =
					isonum_711(&rr->u.ZF.parms[1]);
				if (block_shift < PAGE_CACHE_SHIFT
						|| block_shift > 17) {
					printk(KERN_WARNING "isofs: "
						"Can't handle ZF block "
						"size of 2^%d\n",
						block_shift);
				} else {
					/*
					 * Note: we don't change
					 * i_blocks here
					 */
					ISOFS_I(inode)->i_file_format =
						isofs_file_compressed;
					/*
					 * Parameters to compression
					 * algorithm (header size,
					 * block size)
					 */
					ISOFS_I(inode)->i_format_parm[0] =
						isonum_711(&rr->u.ZF.parms[0]);
					ISOFS_I(inode)->i_format_parm[1] =
						isonum_711(&rr->u.ZF.parms[1]);
					inode->i_size =
					    isonum_733(rr->u.ZF.
						       real_size);
				}
			} else {
				printk(KERN_WARNING
				       "isofs: Unknown ZF compression "
						"algorithm: %c%c\n",
				       rr->u.ZF.algorithm[0],
				       rr->u.ZF.algorithm[1]);
Andrew Morton's avatar
Andrew Morton committed
428
			}
Andrew Morton's avatar
Andrew Morton committed
429 430 431 432 433
			break;
		}
#endif
		default:
			break;
Andrew Morton's avatar
Andrew Morton committed
434 435 436
		}
	}
	MAYBE_CONTINUE(repeat, inode);
Andrew Morton's avatar
Andrew Morton committed
437
out:
Andrew Morton's avatar
Andrew Morton committed
438 439 440
	if (buffer)
		kfree(buffer);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
}

static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
{
	int slen;
	int rootflag;
	struct SL_component *oldslp;
	struct SL_component *slp;
	slen = rr->len - 5;
	slp = &rr->u.SL.link;
	while (slen > 1) {
		rootflag = 0;
		switch (slp->flags & ~1) {
		case 0:
			if (slp->len > plimit - rpnt)
				return NULL;
			memcpy(rpnt, slp->text, slp->len);
Andrew Morton's avatar
Andrew Morton committed
458
			rpnt += slp->len;
Linus Torvalds's avatar
Linus Torvalds committed
459 460 461 462
			break;
		case 2:
			if (rpnt >= plimit)
				return NULL;
Andrew Morton's avatar
Andrew Morton committed
463
			*rpnt++ = '.';
Linus Torvalds's avatar
Linus Torvalds committed
464 465 466 467
			break;
		case 4:
			if (2 > plimit - rpnt)
				return NULL;
Andrew Morton's avatar
Andrew Morton committed
468 469
			*rpnt++ = '.';
			*rpnt++ = '.';
Linus Torvalds's avatar
Linus Torvalds committed
470 471 472 473 474
			break;
		case 8:
			if (rpnt >= plimit)
				return NULL;
			rootflag = 1;
Andrew Morton's avatar
Andrew Morton committed
475
			*rpnt++ = '/';
Linus Torvalds's avatar
Linus Torvalds committed
476 477 478
			break;
		default:
			printk("Symlink component flag not implemented (%d)\n",
Andrew Morton's avatar
Andrew Morton committed
479
			       slp->flags);
Linus Torvalds's avatar
Linus Torvalds committed
480 481 482
		}
		slen -= slp->len + 2;
		oldslp = slp;
Andrew Morton's avatar
Andrew Morton committed
483
		slp = (struct SL_component *)((char *)slp + slp->len + 2);
Linus Torvalds's avatar
Linus Torvalds committed
484 485 486 487 488 489 490 491 492 493

		if (slen < 2) {
			/*
			 * If there is another SL record, and this component
			 * record isn't continued, then add a slash.
			 */
			if ((!rootflag) && (rr->u.SL.flags & 1) &&
			    !(oldslp->flags & 1)) {
				if (rpnt >= plimit)
					return NULL;
Andrew Morton's avatar
Andrew Morton committed
494
				*rpnt++ = '/';
Linus Torvalds's avatar
Linus Torvalds committed
495 496 497 498 499 500 501 502 503 504
			}
			break;
		}

		/*
		 * If this component record isn't continued, then append a '/'.
		 */
		if (!rootflag && !(oldslp->flags & 1)) {
			if (rpnt >= plimit)
				return NULL;
Andrew Morton's avatar
Andrew Morton committed
505
			*rpnt++ = '/';
Linus Torvalds's avatar
Linus Torvalds committed
506 507 508 509 510
		}
	}
	return rpnt;
}

Andrew Morton's avatar
Andrew Morton committed
511
int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
512
{
Andrew Morton's avatar
Andrew Morton committed
513 514 515 516 517 518 519 520
	int result = parse_rock_ridge_inode_internal(de, inode, 0);
	/* if rockridge flag was reset and we didn't look for attributes
	 * behind eventual XA attributes, have a look there */
	if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1)
	    && (ISOFS_SB(inode->i_sb)->s_rock == 2)) {
		result = parse_rock_ridge_inode_internal(de, inode, 14);
	}
	return result;
Linus Torvalds's avatar
Linus Torvalds committed
521 522 523 524 525 526 527 528
}

/* readpage() for symlinks: reads symlink contents into the page and either
   makes it uptodate and returns 0 or returns error (-EIO) */

static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
{
	struct inode *inode = page->mapping->host;
Andrew Morton's avatar
Andrew Morton committed
529
	struct iso_inode_info *ei = ISOFS_I(inode);
Linus Torvalds's avatar
Linus Torvalds committed
530 531 532 533 534
	char *link = kmap(page);
	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
	struct buffer_head *bh;
	char *rpnt = link;
	unsigned char *pnt;
535
	struct iso_directory_record *raw_de;
536 537 538 539
	int cont_extent = 0;
	int cont_offset = 0;
	int cont_size = 0;
	void *buffer = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
	unsigned long block, offset;
	int sig;
	int len;
	unsigned char *chr;
	struct rock_ridge *rr;

	if (!ISOFS_SB(inode->i_sb)->s_rock)
		goto error;

	block = ei->i_iget5_block;
	lock_kernel();
	bh = sb_bread(inode->i_sb, block);
	if (!bh)
		goto out_noread;

Andrew Morton's avatar
Andrew Morton committed
555 556
	offset = ei->i_iget5_offset;
	pnt = (unsigned char *)bh->b_data + offset;
Linus Torvalds's avatar
Linus Torvalds committed
557

558
	raw_de = (struct iso_directory_record *)pnt;
Linus Torvalds's avatar
Linus Torvalds committed
559 560 561 562 563 564 565 566 567 568

	/*
	 * If we go past the end of the buffer, there is some sort of error.
	 */
	if (offset + *pnt > bufsize)
		goto out_bad_span;

	/* Now test for possible Rock Ridge extensions which will override
	   some of these numbers in the inode structure. */

569
	setup_rock_ridge(raw_de, inode, &chr, &len);
Linus Torvalds's avatar
Linus Torvalds committed
570

Andrew Morton's avatar
Andrew Morton committed
571 572
repeat:
	while (len > 2) { /* There may be one byte for padding somewhere */
Andrew Morton's avatar
Andrew Morton committed
573
		rr = (struct rock_ridge *)chr;
Linus Torvalds's avatar
Linus Torvalds committed
574 575 576 577 578 579 580 581 582 583 584 585 586 587
		if (rr->len < 3)
			goto out;	/* Something got screwed up here */
		sig = isonum_721(chr);
		chr += rr->len;
		len -= rr->len;
		if (len < 0)
			goto out;	/* corrupted isofs */

		switch (sig) {
		case SIG('R', 'R'):
			if ((rr->u.RR.flags[0] & RR_SL) == 0)
				goto out;
			break;
		case SIG('S', 'P'):
Andrew Morton's avatar
Andrew Morton committed
588 589
			if (check_sp(rr, inode))
				goto out;
Linus Torvalds's avatar
Linus Torvalds committed
590 591 592 593 594 595 596 597 598
			break;
		case SIG('S', 'L'):
			rpnt = get_symlink_chunk(rpnt, rr,
						 link + (PAGE_SIZE - 1));
			if (rpnt == NULL)
				goto out;
			break;
		case SIG('C', 'E'):
			/* This tells is if there is a continuation record */
Andrew Morton's avatar
Andrew Morton committed
599 600 601
			cont_extent = isonum_733(rr->u.CE.extent);
			cont_offset = isonum_733(rr->u.CE.offset);
			cont_size = isonum_733(rr->u.CE.size);
Linus Torvalds's avatar
Linus Torvalds committed
602 603 604 605 606
		default:
			break;
		}
	}
	MAYBE_CONTINUE(repeat, inode);
Andrew Morton's avatar
Andrew Morton committed
607
	kfree(buffer);
Linus Torvalds's avatar
Linus Torvalds committed
608 609 610 611 612 613 614 615 616 617 618 619

	if (rpnt == link)
		goto fail;
	brelse(bh);
	*rpnt = '\0';
	unlock_kernel();
	SetPageUptodate(page);
	kunmap(page);
	unlock_page(page);
	return 0;

	/* error exit from macro */
Andrew Morton's avatar
Andrew Morton committed
620
out:
Linus Torvalds's avatar
Linus Torvalds committed
621 622 623
	if (buffer)
		kfree(buffer);
	goto fail;
Andrew Morton's avatar
Andrew Morton committed
624
out_noread:
Linus Torvalds's avatar
Linus Torvalds committed
625 626
	printk("unable to read i-node block");
	goto fail;
Andrew Morton's avatar
Andrew Morton committed
627
out_bad_span:
Linus Torvalds's avatar
Linus Torvalds committed
628
	printk("symlink spans iso9660 blocks\n");
Andrew Morton's avatar
Andrew Morton committed
629
fail:
Linus Torvalds's avatar
Linus Torvalds committed
630 631
	brelse(bh);
	unlock_kernel();
Andrew Morton's avatar
Andrew Morton committed
632
error:
Linus Torvalds's avatar
Linus Torvalds committed
633 634 635 636 637 638 639
	SetPageError(page);
	kunmap(page);
	unlock_page(page);
	return -EIO;
}

struct address_space_operations isofs_symlink_aops = {
Andrew Morton's avatar
Andrew Morton committed
640
	.readpage = rock_ridge_symlink_readpage
Linus Torvalds's avatar
Linus Torvalds committed
641
};