/*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#if NAMEDSTREAMS
/*
- * Cast to 'unsigned int' loses precision - hope that's OK...
+ * We use %p to prevent loss of precision for pointers on varying architectures.
*/
#define MAKE_SHADOW_NAME(VP, NAME) \
- snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(VP)->v_data);
+ snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%p%08x%p", (void*)(VP), (VP)->v_id, (VP)->v_data);
static vnode_t shadow_dvp; /* tmp directory to hold stream shadow files */
static int shadow_vid;
error = default_getnamedstream(vp, svpp, name, op, context);
if (error == 0) {
+ uint32_t streamflags = VISNAMEDSTREAM;
vnode_t svp = *svpp;
+
+ if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
+ streamflags |= VISSHADOW;
+ }
/* Tag the vnode. */
- vnode_lock(svp);
- svp->v_flag |= VISNAMEDSTREAM;
+ vnode_lock_spin(svp);
+ svp->v_flag |= streamflags;
vnode_unlock(svp);
- /* Make the file it's parent. */
+
+ /* Tag the parent so we know to flush credentials for streams on setattr */
+ vnode_lock_spin(vp);
+ vp->v_lflag |= VL_HASSTREAMS;
+ vnode_unlock(vp);
+
+ /* Make the file it's parent.
+ * Note: This parent link helps us distinguish vnodes for
+ * shadow stream files from vnodes for resource fork on file
+ * systems that support namedstream natively (both have
+ * VISNAMEDSTREAM set) by allowing access to mount structure
+ * for checking MNTK_NAMED_STREAMS bit at many places in the
+ * code.
+ */
vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
}
error = default_makenamedstream(vp, svpp, name, context);
if (error == 0) {
+ uint32_t streamflags = VISNAMEDSTREAM;
vnode_t svp = *svpp;
/* Tag the vnode. */
- vnode_lock(svp);
- svp->v_flag |= VISNAMEDSTREAM;
+ if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
+ streamflags |= VISSHADOW;
+ }
+
+ /* Tag the vnode. */
+ vnode_lock_spin(svp);
+ svp->v_flag |= streamflags;
vnode_unlock(svp);
- /* Make the file it's parent. */
+
+ /* Tag the parent so we know to flush credentials for streams on setattr */
+ vnode_lock_spin(vp);
+ vp->v_lflag |= VL_HASSTREAMS;
+ vnode_unlock(vp);
+
+ /* Make the file it's parent.
+ * Note: This parent link helps us distinguish vnodes for
+ * shadow stream files from vnodes for resource fork on file
+ * systems that support namedstream natively (both have
+ * VISNAMEDSTREAM set) by allowing access to mount structure
+ * for checking MNTK_NAMED_STREAMS bit at many places in the
+ * code.
+ */
vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
}
return (error);
/*
* Release a named stream shadow file.
+ *
+ * Note: This function is called from two places where we do not need
+ * to check if the vnode has any references held before deleting the
+ * shadow file. Once from vclean() when the vnode is being reclaimed
+ * and we do not hold any references on the vnode. Second time from
+ * default_getnamedstream() when we get an error during shadow stream
+ * file initialization so that other processes who are waiting for the
+ * shadow stream file initialization by the creator will get opportunity
+ * to create and initialize the file again.
*/
errno_t
vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
{
vnode_t dvp;
struct componentname cn;
- char tmpname[48];
+ char tmpname[80];
errno_t err;
- if (vnode_isinuse(svp, 1)) {
- return (EBUSY);
- }
cache_purge(svp);
vnode_lock(svp);
if (err != 0) {
return err;
}
- /* Check for busy svp one last time. */
- if (vnode_isinuse(svp, 1) == 0) {
- (void) VNOP_REMOVE(dvp, svp, &cn, 0, context);
- (void) vnode_recycle(svp);
- }
+
+ (void) VNOP_REMOVE(dvp, svp, &cn, 0, context);
vnode_put(dvp);
return (0);
if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
return (ENOMEM);
}
- auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
offset = 0;
/*
while (offset < datasize) {
iosize = MIN(datasize - offset, iosize);
- uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ);
+ uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
uio_addiov(auio, (uintptr_t)bufptr, iosize);
error = VNOP_READ(svp, auio, 0, context);
if (error) {
break;
}
}
- uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE);
+ uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)bufptr, iosize);
error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
if (error) {
vnode_t svp = NULLVP;
struct componentname cn;
struct vnode_attr va;
- char tmpname[48];
+ char tmpname[80];
size_t datasize = 0;
int error = 0;
/* Create the shadow stream file. */
error = VNOP_CREATE(dvp, &svp, &cn, &va, context);
if (error == 0) {
+ vnode_recycle(svp);
*creator = 1;
} else if ((error == EEXIST) && !makestream) {
error = VNOP_LOOKUP(dvp, &svp, &cn, context);
/*
* The creator of the shadow file provides its file data,
- * all other threads should wait until its ready.
+ * all other threads should wait until its ready. In order to
+ * prevent a deadlock during error codepaths, we need to check if the
+ * vnode is being created, or if it has failed out. Regardless of success or
+ * failure, we set the VISSHADOW bit on the vnode, so we check that
+ * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
+ * then we can infer the creator isn't done yet. If it's there, but
+ * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
+ * try again.
*/
if (!creator) {
vnode_lock(svp);
vnode_unlock(svp);
goto out;
} else {
- /* its not ready, wait for it (sleep using v_parent as channel) */
- msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
- "getnamedstream", NULL);
+ /* It's not ready, wait for it (sleep using v_parent as channel) */
+ if ((svp->v_flag & VISSHADOW)) {
+ /*
+ * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
+ * thread is done with this vnode. Just unlock the vnode and try again
+ */
+ vnode_unlock(svp);
+ }
+ else {
+ /* Otherwise, sleep if the shadow file is not created yet */
+ msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
+ "getnamedstream", NULL);
+ }
vnode_put(svp);
svp = NULLVP;
goto retry;
goto out;
}
- auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
offset = 0;
error = VNOP_OPEN(svp, 0, context);
iosize = MIN(datasize - offset, iosize);
- uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ);
+ uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
uio_addiov(auio, (uintptr_t)bufptr, iosize);
error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
XATTR_NOSECURITY, context);
break;
}
- uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE);
+ uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)bufptr, iosize);
error = VNOP_WRITE(svp, auio, 0, context);
if (error) {
if (creator) {
if (error == 0) {
vnode_lock(svp);
- svp->v_flag |= VISNAMEDSTREAM;
+ /* VISSHADOW would be set later on anyway, so we set it now */
+ svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
wakeup((caddr_t)&svp->v_parent);
vnode_unlock(svp);
} else {
- /* On post create errors, get rid of shadow file. */
+ /* On post create errors, get rid of the shadow file. This
+ * way if there is another process waiting for initialization
+ * of the shadowfile by the current process will wake up and
+ * retry by creating and initializing the shadow file again.
+ * Also add the VISSHADOW bit here to indicate we're done operating
+ * on this vnode.
+ */
(void)vnode_relenamedstream(vp, svp, context);
-
+ vnode_lock (svp);
+ svp->v_flag |= VISSHADOW;
wakeup((caddr_t)&svp->v_parent);
+ vnode_unlock(svp);
}
}
vnode_t svp = *svpp;
vnode_lock(svp);
- svp->v_flag |= VISNAMEDSTREAM;
+ /* If we're the creator, mark it as a named stream */
+ svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
/* Wakeup any waiters on the v_parent channel */
wakeup((caddr_t)&svp->v_parent);
vnode_unlock(svp);
+
}
+
return (error);
}
vnode_t sdvp = NULLVP;
struct componentname cn;
struct vnode_attr va;
- char tmpname[48];
+ char tmpname[80];
uint32_t tmp_fsid;
int error;
}
}
- /* Obtain the vnode for "/tmp" directory. */
- if (vnode_lookup("/tmp", 0, &dvp, context) != 0) {
+ /* Obtain the vnode for "/var/run" directory. */
+ if (vnode_lookup("/var/run", 0, &dvp, context) != 0) {
error = ENOTSUP;
goto out;
}
/* Create the shadow stream directory. */
- snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%x%x",
- (unsigned int)rootvnode, shadow_sequence);
+ snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%p%x",
+ (void*)rootvnode, shadow_sequence);
bzero(&cn, sizeof(cn));
cn.cn_nameiop = LOOKUP;
cn.cn_flags = ISLASTCN;
if (sdvp->v_type != VDIR) {
goto baddir;
}
- /* Obtain the fsid for /tmp directory */
+ /* Obtain the fsid for /var/run directory */
VATTR_INIT(&va);
VATTR_WANTED(&va, va_fsid);
if (VNOP_GETATTR(dvp, &va, context) != 0 ||
*/
-#pragma options align=mac68k
-
#define FINDERINFOSIZE 32
typedef struct apple_double_entry {
u_int32_t type; /* entry type: see list, 0 invalid */
u_int32_t offset; /* entry data offset from the beginning of the file. */
u_int32_t length; /* entry data length in bytes. */
-} apple_double_entry_t;
+} __attribute__((aligned(2), packed)) apple_double_entry_t;
typedef struct apple_double_header {
apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
u_int8_t pad[2]; /* get better alignment inside attr_header */
-} apple_double_header_t;
+} __attribute__((aligned(2), packed)) apple_double_header_t;
#define ADHDRSIZE (4+4+16+2)
u_int16_t flags;
u_int8_t namelen;
u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
-} attr_entry_t;
+} __attribute__((aligned(2), packed)) attr_entry_t;
/* Header + entries must fit into 64K. Data may extend beyond 64K. */
u_int32_t reserved[3];
u_int16_t flags;
u_int16_t num_attrs;
-} attr_header_t;
+} __attribute__((aligned(2), packed)) attr_header_t;
/* Empty Resource Fork Header */
u_int16_t mh_Types;
u_int16_t mh_Names;
u_int16_t typeCount;
-} rsrcfork_header_t;
+} __attribute__((aligned(2), packed)) rsrcfork_header_t;
#define RF_FIRST_RESOURCE 256
#define RF_NULL_MAP_LENGTH 30
#define RF_EMPTY_TAG "This resource fork intentionally left blank "
-#pragma options align=reset
-
/* Runtime information about the attribute file. */
typedef struct attr_info {
vfs_context_t context;
(void) vnode_setattr(vp, &va, context);
}
}
+
+ post_event_if_success(vp, error, NOTE_ATTRIB);
+
return (error);
}
(void) vnode_setattr(vp, &va, context);
}
}
+
+ post_event_if_success(vp, error, NOTE_ATTRIB);
+
return (error);
}
return (error);
}
if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
+ if (error == ENOATTR)
+ error = 0;
close_xattrfile(xvp, FREAD, context);
return (error);
}
return (error);
}
+/*
+ * Check the header of a ._ file to verify that it is in fact an Apple Double
+ * file. Returns 0 if the header is valid, non-zero if invalid.
+ */
+int check_appledouble_header(vnode_t vp, vfs_context_t ctx)
+{
+ int error = 0;
+ attr_info_t ainfo;
+ struct vnode_attr va;
+ uio_t auio = NULL;
+ void *buffer = NULL;
+ int iosize;
+
+ ainfo.filevp = vp;
+ ainfo.context = ctx;
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_data_size);
+ if ((error = vnode_getattr(vp, &va, ctx))) {
+ goto out;
+ }
+ ainfo.filesize = va.va_data_size;
+
+ iosize = MIN(ATTR_MAX_HDR_SIZE, ainfo.filesize);
+ if (iosize == 0) {
+ error = ENOATTR;
+ goto out;
+ }
+ ainfo.iosize = iosize;
+
+ MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK);
+ if (buffer == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
+ uio_addiov(auio, (uintptr_t)buffer, iosize);
+
+ /* Read the header */
+ error = VNOP_READ(vp, auio, 0, ctx);
+ if (error) {
+ goto out;
+ }
+ ainfo.rawsize = iosize - uio_resid(auio);
+ ainfo.rawdata = (u_int8_t *)buffer;
+
+ error = check_and_swap_apple_double_header(&ainfo);
+ if (error) {
+ goto out;
+ }
+
+ /* If we made it here, then the header is ok */
+
+out:
+ if (auio) {
+ uio_free(auio);
+ }
+ if (buffer) {
+ FREE(buffer, M_TEMP);
+ }
+
+ return error;
+}
+
static int
open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context)
{
error = vn_create(dvp, &nd.ni_vp, &nd.ni_cnd, &va,
VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
context);
- if (error == 0)
- xvp = nd.ni_vp;
+ if (error)
+ error = ENOATTR;
+ else
+ xvp = nd.ni_vp;
}
nameidone(&nd);
if (dvp != vp) {
if (error)
goto out;
} else {
- if ((error = namei(&nd))) {
- nd.ni_dvp = NULLVP;
+ if ((error = namei(&nd))) {
+ nd.ni_dvp = NULLVP;
error = ENOATTR;
- goto out;
+ goto out;
}
xvp = nd.ni_vp;
nameidone(&nd);
locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK;
error = lock_xattrfile(xvp, locktype, context);
+ if (error)
+ error = ENOATTR;
}
out:
if (dvp && (dvp != vp)) {
goto bail;
}
- auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
uio_addiov(auio, (uintptr_t)buffer, iosize);
/* Read the file header. */
/* Read the system data which starts at byte 16 */
- rf_uio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
+ rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData));
uio_setoffset(rf_uio, filehdr->entries[i].offset + 16);
rf_err = VNOP_READ(xvp, rf_uio, 0, context);
attrhdr->num_attrs = 0;
/* Push out new header */
- uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE);
+ uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)filehdr, writesize);
swap_adhdr(filehdr); /* to big endian */
bzero(buffer, ATTR_BUF_SIZE);
xah = (attr_header_t *)buffer;
- auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE);
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
rsrcforksize = sizeof(rsrcfork_header_t);
rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
uio_t auio;
int error;
- auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE);
+ auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
swap_adhdr(ainfop->filehdr);
size_t chunk, orig_chunk;
char *buff;
off_t pos;
- ucred_t ucred = vfs_context_ucred(context);
+ kauth_cred_t ucred = vfs_context_ucred(context);
proc_t p = vfs_context_proc(context);
if (delta == 0 || len == 0) {
break;
}
- if ((pos - chunk) < start) {
+ if ((pos - (off_t)chunk) < start) {
chunk = pos - start;
if (chunk == 0) { // we're all done
char *buff;
off_t pos;
off_t end;
- ucred_t ucred = vfs_context_ucred(context);
+ kauth_cred_t ucred = vfs_context_ucred(context);
proc_t p = vfs_context_proc(context);
if (delta == 0 || len == 0) {
break;
}
- if ((pos + chunk) > end) {
+ if ((pos + (off_t)chunk) > end) {
chunk = end - pos;
if (chunk == 0) { // we're all done