cd9660_util.c   [plain text]


/*
 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/*	$NetBSD: cd9660_util.c,v 1.8 1994/12/13 22:33:25 mycroft Exp $	*/

/*-
 * Copyright (c) 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley
 * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
 * Support code is derived from software contributed to Berkeley
 * by Atsushi Murai (amurai@spec.co.jp).
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)cd9660_util.c	8.3 (Berkeley) 12/5/94
 *
 * HISTORY
 *  7-Dec-98	Add ATTR_VOL_MOUNTFLAGS attribute support - djb
 * 18-Nov-98	Add support for volfs - djb
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <sys/conf.h>
#include <sys/utfconv.h>
#include <miscfs/specfs/specdev.h> /* XXX */
#include <miscfs/fifofs/fifo.h> /* XXX */
#include <sys/malloc.h>
#include <sys/dir.h>
#include <sys/attr.h>
#include <kern/assert.h>

#include <isofs/cd9660/iso.h>
#include <isofs/cd9660/cd9660_node.h>
#include <isofs/cd9660/iso_rrip.h>

#include <libkern/OSByteOrder.h>

/*
 * translate and compare a filename
 * Note: Version number plus ';' may be omitted.
 */
int
isofncmp(u_char *fn, int fnlen, u_char *isofn, int isolen)
{
	int i, j;
	char c;
	
	while (--fnlen >= 0) {
		if (--isolen < 0)
			return *fn;
		if ((c = *isofn++) == ';') {
			switch (*fn++) {
			default:
				return *--fn;
			case 0:
				return 0;
			case ';':
				break;
			}
			for (i = 0; --fnlen >= 0; i = i * 10 + *fn++ - '0') {
				if (*fn < '0' || *fn > '9') {
					return -1;
				}
			}
			for (j = 0; --isolen >= 0; j = j * 10 + *isofn++ - '0');
			return i - j;
		}
		/* if raw comparison fails, check if char was mapped */
		if (c != *fn) {
			if (c >= 'A' && c <= 'Z') {
				if (c + ('a' - 'A') != *fn) {
					if (*fn >= 'a' && *fn <= 'z')
						return *fn - ('a' - 'A') - c;
					else
						return *fn - c;
				}
			} else if (c == '/') {
				if (*fn != ':')
					return *fn - c;
			} else if (c > 0 || *fn != '_')
				return *fn - c;
		}
		fn++;
	}
	if (isolen > 0) {
		switch (*isofn) {
		default:
			return -1;
		case '.':
			if (isofn[1] != ';')
				return -1;
		case ';':
			return 0;
		}
	}
	return 0;
}


/*
 * translate and compare a UCS-2 filename
 * Note: Version number plus ';' may be omitted.
 *
 * The name pointed to by "fn" is the search name, whose characters are
 * in native endian order.  The name "ucsfn" is the on-disk name, whose
 * characters are in big endian order.
 */

int
ucsfncmp(u_int16_t *fn, int fnlen, u_int16_t *ucsfn, int ucslen)
{
	int i, j;
	u_int16_t c;
	
	/* convert byte count to char count */
	ucslen /= 2;
	fnlen /= 2;

	while (--fnlen >= 0) {
		if (--ucslen < 0)
			return *fn;
		if ((c = OSSwapBigToHostInt16(*ucsfn++)) == UCS_SEPARATOR2) {
			switch (*fn++) {
			default:
				return *--fn;
			case 0:
				return 0;
			case UCS_SEPARATOR2:
				break;
			}
			for (i = 0; --fnlen >= 0; i = i * 10 + *fn++ - '0') {
				if (*fn < '0' || *fn > '9') {
					return -1;
				}
			}
			for (j = 0; --ucslen >= 0; j = j * 10 + OSSwapBigToHostInt16(*ucsfn++) - '0');
			return i - j;
		}
		if (c != *fn)
			return *fn - c;
		fn++;
	}
	if (ucslen > 0) {
		switch (*ucsfn) {
		default:
			return -1;
		case OSSwapHostToBigConstInt16(UCS_SEPARATOR1):
			if (ucsfn[1] != OSSwapHostToBigConstInt16(UCS_SEPARATOR2))
				return -1;
		case OSSwapHostToBigConstInt16(UCS_SEPARATOR2):
			return 0;
		}
	}
	return 0;
}


/*
 * translate a filename
 */
void
isofntrans(u_char *infn, int infnlen, u_char *outfn, u_short *outfnlen,
		int original, int assoc)
{
	int fnidx = 0;
	
	/*
	 * Add a "._" prefix for associated files
	 */
	if (assoc) {
		*outfn++ = ASSOCCHAR1;
		*outfn++ = ASSOCCHAR2;
		fnidx += 2;
		infnlen +=2;
	}
	for (; fnidx < infnlen; fnidx++) {
		char c = *infn++;
		
		/*
		 * Some ISO 9600 CD names contain 8-bit chars.
		 * These chars are mapped to '_' because there
		 * is no context for mapping them to UTF-8.
		 * In addition '/' is mapped to ':'.
		 *
		 * isofncmp accounts for these mappings.
		 */
		if (!original) {
			if (c < 0)
				c = '_';
			else if (c == '/')
				c = ':';
			else if (c == '.' && *infn == ';')
				break;
			else if (c == ';')
				break;
		}
		*outfn++ = c;
	}
	*outfnlen = fnidx;
}



/*
 * translate a UCS-2 filename to UTF-8
 */
void
ucsfntrans(u_int16_t *infn, int infnlen, u_char *outfn, u_short *outfnlen,
		int dir, int assoc)
{
	if (infnlen == 1) {
		strcpy(outfn, "..");

		if (*(u_char*)infn == 0)
			*outfnlen = 1;
		else if (*(u_char*)infn == 1) 
			*outfnlen = 2;
	} else {
		int fnidx;
		size_t outbytes;
		int flags;
		
		fnidx = infnlen/2;
		flags = 0;

		/*
		 * Add a "._" prefix for associated files
		 */
		if (assoc) {
			*outfn++ = ASSOCCHAR1;
			*outfn++ = ASSOCCHAR2;
		}
		if (!dir) {
			/* strip file version number */
			for (fnidx--; fnidx > 0; fnidx--) {
				/* stop when ';' is found */
				if (infn[fnidx] == OSSwapHostToBigConstInt16(UCS_SEPARATOR2)) {
					/* drop dangling dot */
					if (fnidx > 0 && infn[fnidx-1] == OSSwapHostToBigConstInt16(UCS_SEPARATOR1))
						fnidx--;
					break;
				}
			}
			if (fnidx <= 0)
				fnidx = infnlen/2;
		}

		flags = UTF_NO_NULL_TERM | UTF_DECOMPOSED | UTF_BIG_ENDIAN;

		(void) utf8_encodestr(infn, fnidx * 2, outfn, &outbytes, ISO_JOLIET_NAMEMAX, 0, flags);
		*outfnlen = assoc ? outbytes + 2 : outbytes;
	}
}


/*
 * count the number of children by enumerating the directory
 */
static int
isochildcount(struct vnode *vdp, int *dircnt, int *filcnt)
{
	struct iso_node *dp;
	struct buf *bp = NULL;
	struct iso_mnt *imp;
	struct iso_directory_record *ep;
	uint32_t bmask;
	int error = 0;
	int reclen;
	int dirs, files;
	int blkoffset;
	int logblksize;
	int32_t diroffset;

	dp = VTOI(vdp);
	imp = dp->i_mnt;
	bmask = imp->im_sector_size - 1;
	logblksize = imp->im_sector_size;
	blkoffset = diroffset = 0;
	dirs = files = 0;

	while (diroffset < dp->i_size) {
		/*
		 * If offset is on a block boundary, read the next 
		 * directory block. Release previous if it exists.
		 */
		if ((diroffset & bmask) == 0) {
			if (bp != NULL)
				buf_brelse(bp);
			if ( (error = cd9660_blkatoff(vdp, SECTOFF(imp, diroffset), NULL, &bp)) )
				break;
			blkoffset = 0;
		}

		ep = (struct iso_directory_record *)
			(buf_dataptr(bp) + blkoffset);

		reclen = isonum_711(ep->length);
		if (reclen == 0) {
			/* skip to next block, if any */
			diroffset =
			    (diroffset & ~bmask) + logblksize;
			continue;
		}

		if ((reclen < ISO_DIRECTORY_RECORD_SIZE)  ||
		    (blkoffset + reclen > logblksize)     ||
		    (reclen < ISO_DIRECTORY_RECORD_SIZE + isonum_711(ep->name_len))){
			/* illegal, so give up */
			break;
		}

		/*
		 * Some poorly mastered discs have an incorrect directory
		 * file size.  If the '.' entry has a better size (bigger)
		 * then use that instead.
		 */
		if ((diroffset == 0) && (isonum_733(ep->size) > dp->i_size)) {
			dp->i_size = isonum_733(ep->size);
		}

		if ( isonum_711(ep->flags) & directoryBit )
			dirs++;
		else if ((isonum_711(ep->flags) & associatedBit) == 0)
			files++;

		diroffset += reclen;
		blkoffset += reclen;
	}

	if (bp)
		buf_brelse (bp);

	*dircnt = dirs;
	*filcnt = files;

	return (error);
}


static uint32_t
DerivePermissionSummary(uid_t owner, gid_t group, mode_t obj_mode, __unused struct iso_mnt *imp)
{
    kauth_cred_t cred = kauth_cred_get();
    uint32_t permissions;
    int is_member;

     /* User id 0 (root) always gets access. */
     if (!suser(cred, NULL)) {
         permissions = R_OK | X_OK;
         goto Exit;
     };

    /* Otherwise, check the owner. */
    if (owner == kauth_cred_getuid(cred)) {
        permissions = ((uint32_t)obj_mode & S_IRWXU) >> 6;
        goto Exit;
    }

    /* Otherwise, check the groups. */
		if (kauth_cred_ismember_gid(cred, group, &is_member) == 0 && is_member) {
			permissions = ((uint32_t)obj_mode & S_IRWXG) >> 3;
			goto Exit;
		}

    /* Otherwise, settle for 'others' access. */
    permissions = (uint32_t)obj_mode & S_IRWXO;

Exit:
	return permissions & ~W_OK;    	/* Write access is always impossible */
}