Exemple #1
0
static int
vniocattach_shadow(struct vn_softc *vn, struct vn_ioctl_64 *vniop, 
				   __unused dev_t dev, int in_kernel, proc_t p)
{
	vfs_context_t ctx = vfs_context_current();
	struct nameidata nd;
	int error, flags;
	shadow_map_t *	map;
	off_t file_size;

	flags = FREAD|FWRITE;
	if (in_kernel) {
		NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE, vniop->vn_file, ctx);
	}
	else {
		NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, 
			   (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), 
			   vniop->vn_file, ctx);
	}
	/* vn_open gives both long- and short-term references */
	error = vn_open(&nd, flags, 0);
	if (error) {
		/* shadow MUST be writable! */
		return (error);
	}
	if (nd.ni_vp->v_type != VREG 
	    || (error = vnode_size(nd.ni_vp, &file_size, ctx))) {
		(void)vn_close(nd.ni_vp, flags, ctx);
		vnode_put(nd.ni_vp);
		return (error ? error : EINVAL);
	}
	map = shadow_map_create(vn->sc_fsize, file_size,
				0, vn->sc_secsize);
	if (map == NULL) {
		(void)vn_close(nd.ni_vp, flags, ctx);
		vnode_put(nd.ni_vp);
		vn->sc_shadow_vp = NULL;
		return (ENOMEM);
	}
	vn->sc_shadow_vp = nd.ni_vp;
	vn->sc_shadow_vid = vnode_vid(nd.ni_vp);
	vn->sc_shadow_vp->v_flag |= VNOCACHE_DATA;
	vn->sc_shadow_map = map;
	vn->sc_flags &= ~VNF_READONLY; /* we're now read/write */

	/* lose the short-term reference */
	vnode_put(nd.ni_vp);
	return(0);
}
Exemple #2
0
kern_return_t 
vnode_pager_get_object_vnode (
	memory_object_t		mem_obj,
	uint32_t * vnodeaddr,
	uint32_t * vid)
{
	vnode_pager_t	vnode_object;

	vnode_object = vnode_pager_lookup(mem_obj);
	if (vnode_object->vnode_handle)  {
		*vnodeaddr = (uint32_t)vnode_object->vnode_handle;
		*vid = (uint32_t)vnode_vid((void *)vnode_object->vnode_handle);	

		return(KERN_SUCCESS);
	}
	
	return(KERN_FAILURE);
}
Exemple #3
0
/*
 * Act like null_hashget, but add passed null_node to hash if no existing
 * node found.
 */
static int
null_hashins(struct mount * mp, struct null_node * xp, struct vnode ** vpp)
{
	struct null_node_hashhead * hd;
	struct null_node * oxp;
	struct vnode * ovp;
	int error = 0;

	hd = NULL_NHASH(xp->null_lowervp);
	lck_mtx_lock(&null_hashmtx);
	LIST_FOREACH(oxp, hd, null_hash)
	{
		if (oxp->null_lowervp == xp->null_lowervp && vnode_mount(NULLTOV(oxp)) == mp) {
			/*
			 * See null_hashget for a description of this
			 * operation.
			 */
			ovp = NULLTOV(oxp);
			if (oxp->null_lowervid != vnode_vid(oxp->null_lowervp)) {
				/*vp doesn't exist so return null (not sure we are actually gonna catch
				 recycle right now
				 This is an exceptional case right now, it suggests the vnode we are
				 trying to add has been recycled
				 don't add it.*/
				error = EIO;
				goto end;
			}
			/* if we found something in the hash map then grab an iocount */
			error = vnode_getwithvid(ovp, oxp->null_myvid);
			if (error == 0) {
				*vpp = ovp;
			}
			goto end;
		}
	}
	/* if it wasn't in the hash map then the vnode pointed to by xp already has a
	 * iocount so don't bother */
	LIST_INSERT_HEAD(hd, xp, null_hash);
	xp->null_flags |= NULL_FLAG_HASHED;
end:
	lck_mtx_unlock(&null_hashmtx);
	return error;
}
Exemple #4
0
/* assumption is that vnode has iocount on it after vnode create */
int
null_getnewvnode(
    struct mount * mp, struct vnode * lowervp, struct vnode * dvp, struct vnode ** vpp, struct componentname * cnp, int root)
{
	struct vnode_fsparam vnfs_param;
	int error             = 0;
	enum vtype type       = VDIR;
	struct null_node * xp = null_nodecreate(lowervp);

	if (xp == NULL) {
		return ENOMEM;
	}

	if (lowervp) {
		type = vnode_vtype(lowervp);
	}

	vnfs_param.vnfs_mp         = mp;
	vnfs_param.vnfs_vtype      = type;
	vnfs_param.vnfs_str        = "nullfs";
	vnfs_param.vnfs_dvp        = dvp;
	vnfs_param.vnfs_fsnode     = (void *)xp;
	vnfs_param.vnfs_vops       = nullfs_vnodeop_p;
	vnfs_param.vnfs_markroot   = root;
	vnfs_param.vnfs_marksystem = 0;
	vnfs_param.vnfs_rdev       = 0;
	vnfs_param.vnfs_filesize   = 0; // set this to 0 since we should only be shadowing non-regular files
	vnfs_param.vnfs_cnp        = cnp;
	vnfs_param.vnfs_flags      = VNFS_ADDFSREF;

	error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vnfs_param, vpp);
	if (error == 0) {
		xp->null_vnode = *vpp;
		xp->null_myvid = vnode_vid(*vpp);
		vnode_settag(*vpp, VT_NULL);
	} else {
		FREE(xp, M_TEMP);
	}
	return error;
}
Exemple #5
0
/*
 * Find the nullfs vnode mapped to lowervp. Return it in *vpp with an iocount if found.
 * Return 0 on success. On failure *vpp will be null and a non-zero error code will be returned.
 */
int
null_hashget(struct mount * mp, struct vnode * lowervp, struct vnode ** vpp)
{
	struct null_node_hashhead * hd;
	struct null_node * a;
	struct vnode * vp;
	int error = ENOENT;

	/*
	 * Find hash base, and then search the (two-way) linked
	 * list looking for a null_node structure which is referencing
	 * the lower vnode. We only give up our reference at reclaim so
	 * just check whether the lowervp has gotten pulled from under us
	 */
	hd = NULL_NHASH(lowervp);
	lck_mtx_lock(&null_hashmtx);
	LIST_FOREACH(a, hd, null_hash)
	{
		if (a->null_lowervp == lowervp && vnode_mount(NULLTOV(a)) == mp) {
			vp = NULLTOV(a);
			if (a->null_lowervid != vnode_vid(lowervp)) {
				/*lowervp has reved */
				error = EIO;
			} else {
				/* if we found something then get an iocount on it */
				error = vnode_getwithvid(vp, a->null_myvid);
				if (error == 0) {
					*vpp = vp;
				}
			}
			break;
		}
	}
	lck_mtx_unlock(&null_hashmtx);
	return error;
}
Exemple #6
0
/**
 * ntfs_inode_hash_get - find or allocate, and return a loaded ntfs inode
 *
 * Search the ntfs inode hash for the ntfs inode matching @na and if present
 * return it.
 *
 * If the found ntfs inode has a vnode attached, then get an iocount reference
 * on the vnode.
 *
 * If not present, allocate the ntfs inode, add it to the hash, and initialize
 * it before returning it.  The inode will be marked NInoAlloc() and no vnode
 * will be attached yet.
 */
ntfs_inode *ntfs_inode_hash_get(ntfs_volume *vol, const ntfs_attr *na)
{
	ntfs_inode_list_head *list;
	ntfs_inode *ni, *nni;

	ntfs_debug("Entering for mft_no 0x%llx, type 0x%x, name_len 0x%x.",
			(unsigned long long)na->mft_no,
			(unsigned)le32_to_cpu(na->type), na->name_len);
	list = ntfs_inode_hash_list(vol, na->mft_no);
	ni = ntfs_inode_hash_list_find(vol, list, na);
	if (ni) {
		ntfs_debug("Done (ntfs_inode found in cache).");
		return ni;
	}
	/* Not found, allocate a new ntfs_inode and initialize it. */
	nni = malloc(sizeof(ntfs_inode), M_NTFS, M_WAITOK);
	if (!nni) {
		ntfs_error(vol->mp, "Failed to allocate new ntfs_inode.");
		return nni;
	}
	if (ntfs_inode_init(vol, nni, na)) {
		free(nni, M_NTFS);
		ntfs_error(vol->mp, "Failed to initialize new ntfs_inode.");
		return NULL;
	}
	/*
	 * Take the hash lock and ensure a racing process did not already
	 * allocate the inode by searching for it again in the cache.
	 */
retry:
	mtx_lock(&ntfs_inode_hash_lock);
	ni = ntfs_inode_hash_list_find_nolock(vol, list, na);
	if (ni) {
		/*
		 * Someone else already added the ntfs inode so return that and
		 * throw away ours.
		 */
		vnode_t vn;
		u32 vn_id = 0;

		vn = ni->vn;
		if (vn)
			vn_id = vnode_vid(vn);
		/* Drops the hash lock. */
		ntfs_inode_wait_locked(ni, &ntfs_inode_hash_lock);
		if (vn && vnode_getwithvid(vn, vn_id))
			goto retry;
		free(nni, M_NTFS);
		ntfs_debug("Done (ntfs_inode found in cache - lost race)).");
		return ni;
	}
	/*
	 * We have allocated a new ntfs inode, it is NInoLocked() and
	 * NInoAlloc() and we hold the hash lock so we can now add our inode to
	 * the hash list bucket and drop the hash lock.
	 */
	LIST_INSERT_HEAD(list, nni, hash);
	mtx_unlock(&ntfs_inode_hash_lock);
	/* Add the inode to the list of inodes in the volume. */
	mtx_lock(&vol->inodes_lock);
	LIST_INSERT_HEAD(&vol->inodes, nni, inodes);
	mtx_unlock(&vol->inodes_lock);
	ntfs_debug("Done (new ntfs_inode added to cache).");
	return nni;
}
Exemple #7
0
static int
vniocattach_file(struct vn_softc *vn,
		 struct vn_ioctl_64 *vniop,
		 dev_t dev,
		 int in_kernel,
		 proc_t p)
{
	dev_t	cdev;
	vfs_context_t ctx = vfs_context_current();
	kauth_cred_t cred;
	struct nameidata nd;
	off_t file_size;
	int error, flags;

	flags = FREAD|FWRITE;
	if (in_kernel) {
		NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE, vniop->vn_file, ctx);
	}
	else {
		NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, 
			   (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), 
			   vniop->vn_file, ctx);
	}
	/* vn_open gives both long- and short-term references */
	error = vn_open(&nd, flags, 0);
	if (error) {
		if (error != EACCES && error != EPERM && error != EROFS) {
			return (error);
		}
		flags &= ~FWRITE;
		if (in_kernel) {
			NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, UIO_SYSSPACE, 
			       vniop->vn_file, ctx);
		}
		else {
			NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW, 
				   (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32), 
			       vniop->vn_file, ctx);
		}
		error = vn_open(&nd, flags, 0);
		if (error) {
			return (error);
		}
	}
	if (nd.ni_vp->v_type != VREG) {
		error = EINVAL;
	}
	else {
		error = vnode_size(nd.ni_vp, &file_size, ctx);
	}
	if (error != 0) {
		(void) vn_close(nd.ni_vp, flags, ctx);
		vnode_put(nd.ni_vp);
		return (error);
	}
	cred = kauth_cred_proc_ref(p);
	nd.ni_vp->v_flag |= VNOCACHE_DATA;
	error = setcred(nd.ni_vp, cred);
	if (error) {
		(void)vn_close(nd.ni_vp, flags, ctx);
		vnode_put(nd.ni_vp);
		kauth_cred_unref(&cred);
		return(error);
	}
	vn->sc_secsize = DEV_BSIZE;
	vn->sc_fsize = file_size;
	vn->sc_size = file_size / vn->sc_secsize;
	vn->sc_vp = nd.ni_vp;
	vn->sc_vid = vnode_vid(nd.ni_vp);
	vn->sc_open_flags = flags;
	vn->sc_cred = cred;
	cdev = makedev(vndevice_cdev_major, minor(dev));
	vn->sc_cdev = devfs_make_node(cdev, DEVFS_CHAR,
				      UID_ROOT, GID_OPERATOR, 
				      0600, "rvn%d", 
				      minor(dev));
	vn->sc_flags |= VNF_INITED;
	if (flags == FREAD)
		vn->sc_flags |= VNF_READONLY;
	/* lose the short-term reference */
	vnode_put(nd.ni_vp);
	return(0);
}
Exemple #8
0
/* always called with union lock held */
void
union_updatevp(struct union_node *un, struct vnode *uppervp,
		struct vnode *lowervp)
{
	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
	int nhash = UNION_HASH(uppervp, lowervp);
	int docache = (lowervp != NULLVP || uppervp != NULLVP);
	int lhash, uhash;
	vnode_t freevp;
	vnode_t freedirvp;
	caddr_t freepath;

	/*
	 * Ensure locking is ordered from lower to higher
	 * to avoid deadlocks.
	 */
	if (nhash < ohash) {
		lhash = nhash;
		uhash = ohash;
	} else {
		lhash = ohash;
		uhash = nhash;
	}

	if (lhash != uhash) {
		while (union_list_lock(lhash))
			continue;
	}

	while (union_list_lock(uhash))
		continue;

	if (ohash != nhash || !docache) {
		if (un->un_flags & UN_CACHED) {
			un->un_flags &= ~UN_CACHED;
			LIST_REMOVE(un, un_cache);
		}
	}

	if (ohash != nhash)
		union_list_unlock(ohash);

	if (un->un_lowervp != lowervp) {
		freevp = freedirvp = NULLVP;
		freepath = (caddr_t)0;
		if (un->un_lowervp) {
			freevp = un->un_lowervp;
			un->un_lowervp = lowervp;
			if (un->un_path) {
				freepath = un->un_path; 
				un->un_path = 0;
			}
			if (un->un_dirvp) {
				freedirvp = un->un_dirvp;
				un->un_dirvp = NULLVP;
			}
			union_unlock();
			if (freevp)
				vnode_put(freevp);
			if (freedirvp)
				vnode_put(freedirvp);
			if (freepath)
				_FREE(un->un_path, M_TEMP);
			union_lock();
		} else 
			un->un_lowervp = lowervp;
		if (lowervp != NULLVP)
			un->un_lowervid = vnode_vid(lowervp);
		un->un_lowersz = VNOVAL;
	}

	if (un->un_uppervp != uppervp) {
		freevp = NULLVP;
		if (un->un_uppervp) {
			freevp = un->un_uppervp;
		}
		un->un_uppervp = uppervp;
		if (uppervp != NULLVP)
			un->un_uppervid = vnode_vid(uppervp);
		un->un_uppersz = VNOVAL;
		union_unlock();
		if (freevp)
			vnode_put(freevp);
		union_lock();
	}

	if (docache && (ohash != nhash)) {
		LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
		un->un_flags |= UN_CACHED;
	}

	union_list_unlock(nhash);
}
Exemple #9
0
static int
vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
    uint64_t *ashift)
{
	vdev_file_t *vf;
	vnode_t *vp;
	vattr_t vattr;
	int error = 0;
    vnode_t *rootdir;

	/*
	 * We must have a pathname, and it must be absolute.
	 */
	if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
		vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
		return (EINVAL);
	}

	/*
	 * Reopen the device if it's not currently open.  Otherwise,
	 * just update the physical size of the device.
	 */
	if (vd->vdev_tsd != NULL) {
		ASSERT(vd->vdev_reopening);
		vf = vd->vdev_tsd;
        vnode_getwithvid(vf->vf_vnode, vf->vf_vid);
		goto skip_open;
	}

	vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_PUSHPAGE);

	/*
	 * We always open the files from the root of the global zone, even if
	 * we're in a local zone.  If the user has gotten to this point, the
	 * administrator has already decided that the pool should be available
	 * to local zone users, so the underlying devices should be as well.
	 */
	ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/');

    /*
      vn_openat(char *pnamep,
      enum uio_seg seg,
      int filemode,
      int createmode,
      struct vnode **vpp,
      enum create crwhy,
      mode_t umask,
      struct vnode *startvp)
      extern int vn_openat(char *pnamep, enum uio_seg seg, int filemode,
      int createmode, struct vnode **vpp, enum create crwhy,
      mode_t umask, struct vnode *startvp);
    */

    rootdir = getrootdir();

    error = vn_openat(vd->vdev_path + 1,
                      UIO_SYSSPACE,
                      spa_mode(vd->vdev_spa) | FOFFMAX,
                      0,
                      &vp,
                      0,
                      0,
                      rootdir
                      );

	if (error) {
		vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
		return (error);
	}

	vf->vf_vnode = vp;
    vf->vf_vid = vnode_vid(vp);

#ifdef _KERNEL
	/*
	 * Make sure it's a regular file.
	 */
	if (!vnode_isreg(vp)) {
        vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
        vnode_put(vf->vf_vnode);
        return (ENODEV);
    }

#endif

skip_open:
	/*
	 * Determine the physical size of the file.
	 */
	vattr.va_mask = AT_SIZE;
	error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred, NULL);
	if (error) {
		vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
        vnode_put(vf->vf_vnode);
		return (error);
	}

	*max_psize = *psize = vattr.va_size;
	*ashift = SPA_MINBLOCKSHIFT;
    vnode_put(vf->vf_vnode);

	return (0);
}
Exemple #10
0
/*
 * Finds the procfsnode_t for a node with a given id and referencing a given structure node
 * on a given instance of the file system. If the node does not already exist, it is created, 
 * entered into the node has table and a vnode is created and attached to it. If the node already 
 * exists, it is returned along with its vnode. In both cases, the vnode has an additional iocount,
 * which the caller must remove at some point by calling vnode_put().
 *
 * Creation of a vnode cannot be performed by this function because the information required 
 * to initialize it is known only to the caller. The caller must supply a pointer to a function
 * that will create the vnode when required, along with an opaque context pointer that is passed
 * to the creation function, along with a pointer to the corresponding procfsnode_t. The creation
 * function must either create the vnode and link it to the procfsnode_t or return an error.
 *
 * The allocation of the procfsnode_t that is done here is reversed in the procfs_reclaim() function,
 * which is called when the node's associated vnode is being reclaimed.
 */
int
procfsnode_find(procfs_mount_t *pmp, procfsnode_id_t node_id, procfs_structure_node_t *snode,
                procfsnode_t **pnpp, vnode_t *vnpp,
                create_vnode_func create_vnode_func,
                void *create_vnode_params) {
    int error = 0;
    boolean_t locked = TRUE;
    procfsnode_t *target_procfsnode = NULL;     // This is the node that we will return.
    procfsnode_t *new_procfsnode = NULL;        // Newly allocated node. Will be freed if not used.
    vnode_t target_vnode = NULL;                // Start by assuming we will not get a vnode.
    int32_t mount_id = pmp->pmnt_id;            // File system id.
    
    // Lock the hash table. We'll keep this locked until we are done,
    // unless we need to allocate memory. In that case, we'll drop the
    // lock, but we'll have to revisit all of our assumptions when we
    // reacquire it, because another thread may have created the node
    // we are looking for.
    lck_mtx_lock(procfsnode_hash_mutex);
    
    boolean_t done = FALSE;
    while (!done) {
        assert(locked);
        error = 0;
        
        // Select the correct hash bucket and walk along it, looking for an existing
        // node with the correct attributes.
        int nodehash = HASH_FOR_MOUNT_AND_ID(mount_id, node_id);
        procfs_hash_head *hash_bucket = PROCFS_NODE_HASH_TO_BUCKET_HEADER(nodehash);
        LIST_FOREACH(target_procfsnode, hash_bucket, node_hash) {
            if (target_procfsnode->node_mnt_id == mount_id
                    && target_procfsnode->node_id.nodeid_pid == node_id.nodeid_pid
                    &&target_procfsnode->node_id.nodeid_objectid == node_id.nodeid_objectid
                    && target_procfsnode->node_id.nodeid_base_id == node_id.nodeid_base_id) {
                // Matched.
                break;
            }
        }
        
        // We got a match if target_procfsnode is not NULL.
        if (target_procfsnode == NULL) {
            // We did not find a match, so either allocate a new node or use the
            // one we created last time around this loop.
            if (new_procfsnode == NULL) {
                // We need to allocate a new node. Before doing that, unlock
                // the node hash, because the memory allocation may block.
                lck_mtx_unlock(procfsnode_hash_mutex);
                locked = FALSE;
                
                new_procfsnode = (procfsnode_t *)OSMalloc(sizeof(procfsnode_t), procfs_osmalloc_tag);
                if (new_procfsnode == NULL) {
                    // Allocation failure - bail. Nothing to clean up and
                    // we don't hold the lock.
                    error = ENOMEM;
                    break;
                }
                
                // We got a new procfsnode. Relock the node hash, then go around the
                // loop again. This is necessary because someone else may have created
                // the same node after we dropped the lock. If that's the case, we'll
                // find that node next time around and we'll use it. The one we just
                // allocated will remain in target_procfsnode and will be freed before we return.
                lck_mtx_lock(procfsnode_hash_mutex);
                locked = TRUE;
                continue;
            } else {
                // If we get here, we know that we need to use the node that we
                // allocated last time around the loop, so promote it to target_procfsnode
                assert(locked);
                assert(new_procfsnode != NULL);
                
                target_procfsnode = new_procfsnode;
                
                // Initialize the new node.
                memset(target_procfsnode, 0, sizeof(procfsnode_t));
                target_procfsnode->node_mnt_id = mount_id;
                target_procfsnode->node_id = node_id;
                target_procfsnode->node_structure_node = snode;
                
                // Add the node to the node hash. We already know which bucket
                // it belongs to.
                LIST_INSERT_HEAD(hash_bucket, target_procfsnode, node_hash);
            }
        }
        
        // At this point, we have a procfsnode_t, which either already existed
        // or was just created. We also have the lock for the node hash table.
        assert(target_procfsnode != NULL);
        assert(locked);
        
        // Check whether another thread is already in the process of creating a
        // vnode for this procfsnode_t. If it is, wait until it's done and go
        // around the loop again.
        if (target_procfsnode->node_attaching_vnode) {
            // Indicate that a wakeup is needed when the attaching thread
            // is done.
            target_procfsnode->node_thread_waiting_attach = TRUE;
            
            // Sleeping will drop and relock the mutex.
            msleep(target_procfsnode, procfsnode_hash_mutex, PINOD, "procfsnode_find", NULL);
            
            // Since anything can have changed while we were away, go around
            // the loop again.
            continue;
        }
        
        target_vnode = target_procfsnode->node_vnode;
        if (target_vnode != NULL) {
            // We already have a vnode. We need to check if it has been reassigned.
            // To do that, unlock and check the vnode id.
            uint32_t vid = vnode_vid(target_vnode);
            lck_mtx_unlock(procfsnode_hash_mutex);
            locked = FALSE;
            
            error = vnode_getwithvid(target_vnode, vid);
            if (error != 0) {
                // Vnode changed identity, so we need to redo everything. Relock
                // because we are expected to hold the lock at the top of the loop.
                // Getting here means that the vnode was reclaimed and the procfsnode
                // was removed from the hash and freed, so we will be restarting from scratch.
                lck_mtx_lock(procfsnode_hash_mutex);
                target_procfsnode = NULL;
                new_procfsnode = NULL;
                locked = TRUE;
                continue;
            }
            
            // The vnode was still present and has not changed id. All we need to do
            // is terminate the loop. We don't hold the lock, "locked" is FALSE and
            // we don't need to relock (and indeed doing so would introduce yet more
            // race conditions). vnode_getwithvid() added an iocount reference for us,
            // which the caller is expected to eventually release with vnode_put().
            assert(error == 0);
            break;
        }
        
        // At this point, we have procfsnode_t in the node hash, but we don't have a
        // vnode. To create the vnode, we have to release the node hash lock and invoke
        // the caller's create_vnode_func callback. Before doing that, we need to set
        // node_attaching_vnode to force any other threads that come in here to wait for
        // this thread to create the vnode (or fail).
        target_procfsnode->node_attaching_vnode = TRUE;
        lck_mtx_unlock(procfsnode_hash_mutex);
        locked = FALSE;
        
        error = (*create_vnode_func)(create_vnode_params, target_procfsnode, &target_vnode);
        assert(error != 0 || target_vnode != NULL);
        
        // Relock the hash table and clear node_attaching_vnode now that we are
        // safely back from the caller's callback.
        lck_mtx_lock(procfsnode_hash_mutex);
        locked = TRUE;
        target_procfsnode->node_attaching_vnode = FALSE;
        
        // If there are threads waiting for the vnode attach to complete,
        // wake them up.
        if (target_procfsnode->node_thread_waiting_attach) {
            target_procfsnode->node_thread_waiting_attach = FALSE;
            wakeup(target_procfsnode);
        }
        
        // Now check whether we succeeded.
        if (error != 0) {
            // Failed to create the vnode -- this is fatal.
            // Remove the procfsnode_t from the hash table and
            // release it.
            procfsnode_free_node(target_procfsnode);
            new_procfsnode = NULL; // To avoid double free.
            break;
        }
        
        // We got the new vnode and it's already linked to the procfsnode_t.
        // Link the procfsnode_t to it. Also add a file system reference to
        // the vnode itself.
        target_procfsnode->node_vnode = target_vnode;
        vnode_addfsref(target_vnode);
        
        break;
    }
    
    // Unlock the hash table, if it is still locked.
    if (locked) {
        lck_mtx_unlock(procfsnode_hash_mutex);
    }
    
    // Free the node we allocated, if we didn't use it. We do this
    // *after* releasing the hash lock just in case it might block.
    if (new_procfsnode != NULL && new_procfsnode != target_procfsnode) {
        OSFree(new_procfsnode, sizeof(procfsnode_t), procfs_osmalloc_tag);
    }
    
    // Set the return value, or NULL if we failed.
    *pnpp = error == 0 ? target_procfsnode : NULL;
    *vnpp = error == 0 ? target_vnode : NULL;
    
    return error;
}
Exemple #11
0
Fichier : gfs.c Projet : RJVB/zfs
/*
 * gfs_file_create(): create a new GFS file
 *
 *   size	- size of private data structure (v_data)
 *   pvp	- parent vnode (GFS directory)
 *   ops	- vnode operations vector
 *
 * In order to use this interface, the parent vnode must have been created by
 * gfs_dir_create(), and the private data stored in v_data must have a
 * 'gfs_file_t' as its first field.
 *
 * Given these constraints, this routine will automatically:
 *
 * 	- Allocate v_data for the vnode
 * 	- Initialize necessary fields in the vnode
 * 	- Hold the parent
 */
struct vnode *
gfs_file_create(size_t size, struct vnode *pvp, vfs_t *vfs, vnodeops_t *ops, enum vtype type, int flags)
{
	gfs_file_t *fp;
	struct vnode *vp;
	struct vnode_fsparam vfsp;

    dprintf("gfs_file_create: vtype %d\n", type);

	/*
	 * Allocate vnode and internal data structure
	 */
	fp = kmem_zalloc(size, KM_SLEEP);


   	bzero(&vfsp, sizeof (vfsp));
	vfsp.vnfs_str = "zfs";
	vfsp.vnfs_mp = vfs;
	vfsp.vnfs_vtype = type;
	vfsp.vnfs_fsnode = fp;
	vfsp.vnfs_flags = VNFS_ADDFSREF;

    vfsp.vnfs_vops = ops;
    if (flags & ZFS_VNODE_SYSTEM)
        vfsp.vnfs_marksystem = 1;
    if (flags & ZFS_VNODE_ROOT)
        vfsp.vnfs_markroot = 1;
	//error = getnewvnode("zfs", vfsp, ops, &vp);

	ASSERT(error == 0);
	//vn_lock(vp, /*LK_EXCLUSIVE |*/ LK_RETRY);
	//vp->v_data = (caddr_t)fp;
    //vnode_setfsnode(vp, fp);


    //mutex_enter(&zfsvfs->z_vnode_create_lock);
    while (vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vp) != 0);
    //mutex_exit(&zfsvfs->z_vnode_create_lock);
	vnode_settag(vp, VT_ZFS);
    dprintf("new vnode %p system %d root %d: vfs %p\n",
           vp, vfsp.vnfs_marksystem, vfsp.vnfs_markroot, vfs);

	/*
	 * Set up various pointers
	 */
	fp->gfs_ino = vnode_vid(vp);
	fp->gfs_vnode = vp;
	fp->gfs_parent = pvp;
	fp->gfs_size = size;
	fp->gfs_type = GFS_FILE;

	//vp->v_vflag |= VV_FORCEINSMQ;
	//error = insmntque(vp, vfsp);
	//vp->v_vflag &= ~VV_FORCEINSMQ;
	//KASSERT(error == 0, ("insmntque() failed: error %d", error));

	/*
	 * Initialize vnode and hold parent.
	 */
	if (pvp) {
        //printf("Skipping lock of parent %p\n", pvp);
        VN_HOLD(pvp);
        dprintf("parent hold pvp %p to 0 -> %d\n", pvp, ((uint32_t *)pvp)[23]);
    }
	return (vp);
}
Exemple #12
0
/*
 * Mount null layer
 */
static int
nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx)
{
	int error                 = 0;
	struct vnode *lowerrootvp = NULL, *vp = NULL;
	struct vfsstatfs * sp   = NULL;
	struct null_mount * xmp = NULL;
	char data[MAXPATHLEN];
	size_t count;
	struct vfs_attr vfa;
	/* set defaults (arbitrary since this file system is readonly) */
	uint32_t bsize  = BLKDEV_IOSIZE;
	size_t iosize   = BLKDEV_IOSIZE;
	uint64_t blocks = 4711 * 4711;
	uint64_t bfree  = 0;
	uint64_t bavail = 0;
	uint64_t bused  = 4711;
	uint64_t files  = 4711;
	uint64_t ffree  = 0;

	kauth_cred_t cred = vfs_context_ucred(ctx);

	NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp, vfs_flags(mp));

	if (vfs_flags(mp) & MNT_ROOTFS)
		return (EOPNOTSUPP);

	/*
	 * Update is a no-op
	 */
	if (vfs_isupdate(mp)) {
		return ENOTSUP;
	}

	/* check entitlement */
	if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
		return EPERM;
	}

	/*
	 * Get argument
	 */
	error = copyinstr(user_data, data, MAXPATHLEN - 1, &count);
	if (error) {
		NULLFSDEBUG("nullfs: error copying data form user %d\n", error);
		goto error;
	}

	/* This could happen if the system is configured for 32 bit inodes instead of
	 * 64 bit */
	if (count > MAX_MNT_FROM_LENGTH) {
		error = EINVAL;
		NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count, MAX_MNT_FROM_LENGTH);
		goto error;
	}

	error = vnode_lookup(data, 0, &lowerrootvp, ctx);
	if (error) {
		NULLFSDEBUG("lookup %s -> %d\n", data, error);
		goto error;
	}

	/* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
	   Keep this to signal what we want to keep around the thing we are mirroring.
	   Drop it in unmount.*/
	error = vnode_ref(lowerrootvp);
	vnode_put(lowerrootvp);
	if (error)
	{
		// If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
		lowerrootvp = NULL;
		goto error;
	}

	NULLFSDEBUG("mount %s\n", data);

	MALLOC(xmp, struct null_mount *, sizeof(*xmp), M_TEMP, M_WAITOK | M_ZERO);
	if (xmp == NULL) {
		error = ENOMEM;
		goto error;
	}

	/*
	 * Save reference to underlying FS
	 */
	xmp->nullm_lowerrootvp  = lowerrootvp;
	xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp);

	error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1);
	if (error) {
		goto error;
	}

	/* vp has an iocount on it from vnode_create. drop that for a usecount. This
	 * is our root vnode so we drop the ref in unmount
	 *
	 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
	vnode_ref(vp);
	vnode_put(vp);

	error = nullfs_init_lck(&xmp->nullm_lock);
	if (error) {
		goto error;
	}

	xmp->nullm_rootvp = vp;

	/* read the flags the user set, but then ignore some of them, we will only
	   allow them if they are set on the lower file system */
	uint64_t flags      = vfs_flags(mp) & (~(MNT_IGNORE_OWNERSHIP | MNT_LOCAL));
	uint64_t lowerflags = vfs_flags(vnode_mount(lowerrootvp)) & (MNT_LOCAL | MNT_QUARANTINE | MNT_IGNORE_OWNERSHIP | MNT_NOEXEC);

	if (lowerflags) {
		flags |= lowerflags;
	}

	/* force these flags */
	flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY);
	vfs_setflags(mp, flags);

	vfs_setfsprivate(mp, xmp);
	vfs_getnewfsid(mp);
	vfs_setlocklocal(mp);

	/* fill in the stat block */
	sp = vfs_statfs(mp);
	strlcpy(sp->f_mntfromname, data, MAX_MNT_FROM_LENGTH);

	sp->f_flags = flags;

	xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */

	error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
	if (error == 0) {
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) {
			bsize = vfa.f_bsize;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) {
			iosize = vfa.f_iosize;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) {
			blocks = vfa.f_blocks;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) {
			bfree = vfa.f_bfree;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) {
			bavail = vfa.f_bavail;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) {
			bused = vfa.f_bused;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) {
			files = vfa.f_files;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) {
			ffree = vfa.f_ffree;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
			if ((vfa.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE)) &&
			    (vfa.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE))) {
				xmp->nullm_flags &= ~NULLM_CASEINSENSITIVE;
			}
		}
	} else {
		goto error;
	}

	sp->f_bsize  = bsize;
	sp->f_iosize = iosize;
	sp->f_blocks = blocks;
	sp->f_bfree  = bfree;
	sp->f_bavail = bavail;
	sp->f_bused  = bused;
	sp->f_files  = files;
	sp->f_ffree  = ffree;

	/* Associate the mac label information from the mirrored filesystem with the
	 * mirror */
	MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp));

	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname);
	return (0);

error:
	if (xmp) {
		FREE(xmp, M_TEMP);
	}
	if (lowerrootvp) {
		vnode_getwithref(lowerrootvp);
		vnode_rele(lowerrootvp);
		vnode_put(lowerrootvp);
	}
	if (vp) {
		/* we made the root vnode but the mount is failed, so clean it up */
		vnode_getwithref(vp);
		vnode_rele(vp);
		/* give vp back */
		vnode_recycle(vp);
		vnode_put(vp);
	}
	return error;
}
Exemple #13
0
/*
 * Look up a vnode/nfsnode by file handle.
 * Callers must check for mount points!!
 * In all cases, a pointer to a
 * nfsnode structure is returned.
 */
int
nfs_nget(
	mount_t mp,
	nfsnode_t dnp,
	struct componentname *cnp,
	u_char *fhp,
	int fhsize,
	struct nfs_vattr *nvap,
	u_int64_t *xidp,
	uint32_t auth,
	int flags,
	nfsnode_t *npp)
{
	nfsnode_t np;
	struct nfsnodehashhead *nhpp;
	vnode_t vp;
	int error, nfsvers;
	mount_t mp2;
	struct vnode_fsparam vfsp;
	uint32_t vid;

	FSDBG_TOP(263, mp, dnp, flags, npp);

	/* Check for unmount in progress */
	if (!mp || vfs_isforce(mp)) {
		*npp = NULL;
		error = ENXIO;
		FSDBG_BOT(263, mp, dnp, 0xd1e, error);
		return (error);
	}
	nfsvers = VFSTONFS(mp)->nm_vers;

	nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
loop:
	lck_mtx_lock(nfs_node_hash_mutex);
	for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
		mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np);
		if (mp != mp2 || np->n_fhsize != fhsize ||
		    bcmp(fhp, np->n_fhp, fhsize))
			continue;
		if (nvap && (nvap->nva_flags & NFS_FFLAG_TRIGGER_REFERRAL) &&
		    cnp && (cnp->cn_namelen > (fhsize - (int)sizeof(dnp)))) {
			/* The name was too long to fit in the file handle.  Check it against the node's name. */
			int namecmp = 0;
			const char *vname = vnode_getname(NFSTOV(np));
			if (vname) {
				if (cnp->cn_namelen != (int)strlen(vname))
					namecmp = 1;
				else
					namecmp = strncmp(vname, cnp->cn_nameptr, cnp->cn_namelen);
				vnode_putname(vname);
			}
			if (namecmp)  /* full name didn't match */
				continue;
		}
		FSDBG(263, dnp, np, np->n_flag, 0xcace0000);
		/* if the node is locked, sleep on it */
		if ((np->n_hflag & NHLOCKED) && !(flags & NG_NOCREATE)) {
			np->n_hflag |= NHLOCKWANT;
			FSDBG(263, dnp, np, np->n_flag, 0xcace2222);
			msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL);
			FSDBG(263, dnp, np, np->n_flag, 0xcace3333);
			goto loop;
		}
		vp = NFSTOV(np);
		vid = vnode_vid(vp);
		lck_mtx_unlock(nfs_node_hash_mutex);
		if ((error = vnode_getwithvid(vp, vid))) {
			/*
			 * If vnode is being reclaimed or has already
			 * changed identity, no need to wait.
			 */
			FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error);
			return (error);
		}
		if ((error = nfs_node_lock(np))) {
			/* this only fails if the node is now unhashed */
			/* so let's see if we can find/create it again */
			FSDBG(263, dnp, *npp, 0xcaced1e2, error);
			vnode_put(vp);
			if (flags & NG_NOCREATE) {
				*npp = 0;
				FSDBG_BOT(263, dnp, *npp, 0xcaced1e0, ENOENT);
				return (ENOENT);
			}
			goto loop;
		}
		/* update attributes */
		if (nvap)
			error = nfs_loadattrcache(np, nvap, xidp, 0);
		if (error) {
			nfs_node_unlock(np);
			vnode_put(vp);
		} else {
			if (dnp && cnp && (flags & NG_MAKEENTRY))
				cache_enter(NFSTOV(dnp), vp, cnp);
			/*
			 * Update the vnode if the name/and or the parent has
			 * changed. We need to do this so that if getattrlist is
			 * called asking for ATTR_CMN_NAME, that the "most"
			 * correct name is being returned. In addition for
			 * monitored vnodes we need to kick the vnode out of the
			 * name cache. We do this so that if there are hard
			 * links in the same directory the link will not be
			 * found and a lookup will get us here to return the
			 * name of the current link. In addition by removing the
			 * name from the name cache the old name will not be
			 * found after a rename done on another client or the
			 * server.  The principle reason to do this is because
			 * Finder is asking for notifications on a directory.
			 * The directory changes, Finder gets notified, reads
			 * the directory (which we have purged) and for each
			 * entry returned calls getattrlist with the name
			 * returned from readdir. gettattrlist has to call
			 * namei/lookup to resolve the name, because its not in
			 * the cache we end up here. We need to update the name
			 * so Finder will get the name it called us with.
			 *
			 * We had an imperfect solution with respect to case
			 * sensitivity.  There is a test that is run in
			 * FileBuster that does renames from some name to
			 * another name differing only in case. It then reads
			 * the directory looking for the new name, after it
			 * finds that new name, it ask gettattrlist to verify
			 * that the name is the new name.  Usually that works,
			 * but renames generate fsevents and fseventsd will do a
			 * lookup on the name via lstat. Since that test renames
			 * old name to new name back and forth there is a race
			 * that an fsevent will be behind and will access the
			 * file by the old name, on a case insensitive file
			 * system that will work. Problem is if we do a case
			 * sensitive compare, we're going to change the name,
			 * which the test's getattrlist verification step is
			 * going to fail. So we will check the case sensitivity
			 * of the file system and do the appropriate compare. In
			 * a rare instance for non homogeneous file systems
			 * w.r.t. pathconf we will use case sensitive compares.
			 * That could break if the file system is actually case
			 * insensitive.
			 *
			 * Note that V2 does not know the case, so we just
			 * assume case sensitivity. 
			 *
			 * This is clearly not perfect due to races, but this is
			 * as good as its going to get. You can defeat the
			 * handling of hard links simply by doing:
			 *
			 *	while :; do ls -l > /dev/null; done
			 *
			 * in a terminal window. Even a single ls -l can cause a
			 * race.
			 *
			 * <rant>What we really need is for the caller, that
			 * knows the name being used is valid since it got it
			 * from a readdir to use that name and not ask for the
			 * ATTR_CMN_NAME</rant>
			 */
			if (dnp && cnp && (vp != NFSTOV(dnp))) {
				int update_flags = (vnode_ismonitored((NFSTOV(dnp)))) ? VNODE_UPDATE_CACHE : 0;
				int (*cmp)(const char *s1, const char *s2, size_t n);

				cmp = nfs_case_insensitive(mp) ? strncasecmp : strncmp;

				if (vp->v_name && cnp->cn_namelen && (*cmp)(cnp->cn_nameptr, vp->v_name, cnp->cn_namelen))
					update_flags |= VNODE_UPDATE_NAME;
				if ((vp->v_name == NULL && cnp->cn_namelen != 0) || (vp->v_name != NULL && cnp->cn_namelen == 0))
					update_flags |= VNODE_UPDATE_NAME;
				if (vnode_parent(vp) != NFSTOV(dnp))
					update_flags |= VNODE_UPDATE_PARENT;
				if (update_flags) {
					NFS_NODE_DBG("vnode_update_identity old name %s new name %.*s update flags = %x\n",
						     vp->v_name, cnp->cn_namelen, cnp->cn_nameptr ? cnp->cn_nameptr : "", update_flags);
					vnode_update_identity(vp, NFSTOV(dnp), cnp->cn_nameptr, cnp->cn_namelen, 0, update_flags);
				}
			}

			*npp = np;
		}
		FSDBG_BOT(263, dnp, *npp, 0xcace0000, error);
		return(error);
	}

	FSDBG(263, mp, dnp, npp, 0xaaaaaaaa);

	if (flags & NG_NOCREATE) {
		lck_mtx_unlock(nfs_node_hash_mutex);
		*npp = 0;
		FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOENT);
		return (ENOENT);
	}

	/*
	 * allocate and initialize nfsnode and stick it in the hash
	 * before calling getnewvnode().  Anyone finding it in the
	 * hash before initialization is complete will wait for it.
	 */
	MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK);
	if (!np) {
		lck_mtx_unlock(nfs_node_hash_mutex);
		*npp = 0;
		FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM);
		return (ENOMEM);
	}
	bzero(np, sizeof *np);
	np->n_hflag |= (NHINIT | NHLOCKED);
	np->n_mount = mp;
	np->n_auth = auth;
	TAILQ_INIT(&np->n_opens);
	TAILQ_INIT(&np->n_lock_owners);
	TAILQ_INIT(&np->n_locks);
	np->n_dlink.tqe_next = NFSNOLIST;
	np->n_dreturn.tqe_next = NFSNOLIST;
	np->n_monlink.le_next = NFSNOLIST;

	/* ugh... need to keep track of ".zfs" directories to workaround server bugs */
	if ((nvap->nva_type == VDIR) && cnp && (cnp->cn_namelen == 4) &&
	    (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == 'z') &&
	    (cnp->cn_nameptr[2] == 'f') && (cnp->cn_nameptr[3] == 's'))
		np->n_flag |= NISDOTZFS;
	if (dnp && (dnp->n_flag & NISDOTZFS))
		np->n_flag |= NISDOTZFSCHILD;

	if (dnp && cnp && ((cnp->cn_namelen != 2) ||
	    (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) {
		vnode_t dvp = NFSTOV(dnp);
		if (!vnode_get(dvp)) {
			if (!vnode_ref(dvp))
				np->n_parent = dvp;
			vnode_put(dvp);
		}
	}

	/* setup node's file handle */
	if (fhsize > NFS_SMALLFH) {
		MALLOC_ZONE(np->n_fhp, u_char *,
				fhsize, M_NFSBIGFH, M_WAITOK);
		if (!np->n_fhp) {
			lck_mtx_unlock(nfs_node_hash_mutex);
			FREE_ZONE(np, sizeof *np, M_NFSNODE);
			*npp = 0;
			FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM);
			return (ENOMEM);
		}
	} else {
Exemple #14
0
/*
 * Use the device, fileid pair to find the incore cnode.
 * If no cnode if found one is created
 *
 * If it is in core, but locked, wait for it.
 *
 * If the cnode is C_DELETED, then return NULL since that 
 * inum is no longer valid for lookups (open-unlinked file).
 *
 * If the cnode is C_DELETED but also marked C_RENAMED, then that means
 * the cnode was renamed over and a new entry exists in its place.  The caller
 * should re-drive the lookup to get the newer entry.  In that case, we'll still
 * return NULL for the cnode, but also return GNV_CHASH_RENAMED in the output flags
 * of this function to indicate the caller that they should re-drive.
 */
struct cnode *
hfs_chash_getcnode(struct hfsmount *hfsmp, ino_t inum, struct vnode **vpp, 
				   int wantrsrc, int skiplock, int *out_flags, int *hflags)
{
	struct cnode	*cp;
	struct cnode	*ncp = NULL;
	vnode_t		vp;
	u_int32_t	vid;

	/* 
	 * Go through the hash list
	 * If a cnode is in the process of being cleaned out or being
	 * allocated, wait for it to be finished and then try again.
	 */
loop:
	hfs_chash_lock_spin(hfsmp);

loop_with_lock:
	for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next) {
		if (cp->c_fileid != inum)
			continue;
		/*
		 * Wait if cnode is being created, attached to or reclaimed.
		 */
		if (ISSET(cp->c_hflag, H_ALLOC | H_ATTACH | H_TRANSIT)) {
		        SET(cp->c_hflag, H_WAITING);

			(void) msleep(cp, &hfsmp->hfs_chash_mutex, PINOD,
			              "hfs_chash_getcnode", 0);
			goto loop_with_lock;
		}
		vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
		if (vp == NULL) {
			/*
			 * The desired vnode isn't there so tag the cnode.
			 */
			SET(cp->c_hflag, H_ATTACH);
			*hflags |= H_ATTACH;

			hfs_chash_unlock(hfsmp);
		} else {
			vid = vnode_vid(vp);

			hfs_chash_unlock(hfsmp);

			if (vnode_getwithvid(vp, vid))
		        	goto loop;
		}
		if (ncp) {
			/*
			 * someone else won the race to create
			 * this cnode and add it to the hash
			 * just dump our allocation
			 */
			FREE_ZONE(ncp, sizeof(struct cnode), M_HFSNODE);
			ncp = NULL;
		}

		if (!skiplock) {
			hfs_lock(cp, HFS_FORCE_LOCK);
		}

		/*
		 * Skip cnodes that are not in the name space anymore
		 * we need to check with the cnode lock held because
		 * we may have blocked acquiring the vnode ref or the
		 * lock on the cnode which would allow the node to be
		 * unlinked.
		 *
		 * Don't return a cnode in this case since the inum
		 * is no longer valid for lookups.
		 */
		if ((cp->c_flag & (C_NOEXISTS | C_DELETED)) && !wantrsrc) {
			int renamed = 0;
			if (cp->c_flag & C_RENAMED) {
				renamed = 1;
			}
			if (!skiplock)
				hfs_unlock(cp);
			if (vp != NULLVP) {
				vnode_put(vp);
			} else {
				hfs_chash_lock_spin(hfsmp);
				CLR(cp->c_hflag, H_ATTACH);
				*hflags &= ~H_ATTACH;
				if (ISSET(cp->c_hflag, H_WAITING)) {
					CLR(cp->c_hflag, H_WAITING);
					wakeup((caddr_t)cp);
				}
				hfs_chash_unlock(hfsmp);
			}
			vp = NULL;
			cp = NULL;
			if (renamed) {
				*out_flags = GNV_CHASH_RENAMED;
			}
		}
		*vpp = vp;
		return (cp);
	}

	/* 
	 * Allocate a new cnode
	 */
	if (skiplock && !wantrsrc)
		panic("%s - should never get here when skiplock is set \n", __FUNCTION__);

	if (ncp == NULL) {
		hfs_chash_unlock(hfsmp);

	        MALLOC_ZONE(ncp, struct cnode *, sizeof(struct cnode), M_HFSNODE, M_WAITOK);
		/*
		 * since we dropped the chash lock, 
		 * we need to go back and re-verify
		 * that this node hasn't come into 
		 * existence...
		 */
		goto loop;
	}
Exemple #15
0
/*
 * Use the device, inum pair to find the incore cnode.
 *
 * If it is in core, but locked, wait for it.
 */
struct vnode *
hfs_chash_getvnode(struct hfsmount *hfsmp, ino_t inum, int wantrsrc, int skiplock, int allow_deleted)
{
	struct cnode *cp;
	struct vnode *vp;
	int error;
	u_int32_t vid;

	/* 
	 * Go through the hash list
	 * If a cnode is in the process of being cleaned out or being
	 * allocated, wait for it to be finished and then try again.
	 */
loop:
	hfs_chash_lock_spin(hfsmp);

	for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next) {
		if (cp->c_fileid != inum)
			continue;
		/* Wait if cnode is being created or reclaimed. */
		if (ISSET(cp->c_hflag, H_ALLOC | H_TRANSIT | H_ATTACH)) {
		        SET(cp->c_hflag, H_WAITING);

			(void) msleep(cp, &hfsmp->hfs_chash_mutex, PDROP | PINOD,
			              "hfs_chash_getvnode", 0);
			goto loop;
		}
		/* Obtain the desired vnode. */
		vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
		if (vp == NULLVP)
			goto exit;

		vid = vnode_vid(vp);
		hfs_chash_unlock(hfsmp);

		if ((error = vnode_getwithvid(vp, vid))) {
		        /*
			 * If vnode is being reclaimed, or has
			 * already changed identity, no need to wait
			 */
		        return (NULL);
		}
		if (!skiplock && hfs_lock(cp, HFS_EXCLUSIVE_LOCK) != 0) {
			vnode_put(vp);
			return (NULL);
		}

		/*
		 * Skip cnodes that are not in the name space anymore
		 * we need to check with the cnode lock held because
		 * we may have blocked acquiring the vnode ref or the
		 * lock on the cnode which would allow the node to be
		 * unlinked
		 */
		if (!allow_deleted) {
			if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
				if (!skiplock) {
					hfs_unlock(cp);
				}
				vnode_put(vp);
				return (NULL);
			}			
		}
		return (vp);
	}
exit:
	hfs_chash_unlock(hfsmp);
	return (NULL);
}
Exemple #16
0
/**
 * Get VBoxVFS root vnode.
 *
 * Handle three cases here:
 *  - vnode does not exist yet: create a new one
 *  - currently creating vnode: wait till the end, increment usage count and return existing one
 *  - vnode already created: increment usage count and return existing one
 *  - vnode was failed to create: give a chance to try to re-create it later
 *
 * @param mp        Mount data provided by VFS layer.
 * @param ppVnode   vnode to return.
 * @param pContext  kAuth context needed in order to authentificate mount operation.
 *
 * @return 0 on success or BSD error code otherwise.
 */
static int
vboxvfs_root(struct mount *mp, struct vnode **ppVnode, vfs_context_t pContext)
{
    NOREF(pContext);

    vboxvfs_mount_t *pMount;
    int rc = 0;
    uint32_t vid;

    PDEBUG("Getting root vnode...");

    AssertReturn(mp,      EINVAL);
    AssertReturn(ppVnode, EINVAL);

    pMount = (vboxvfs_mount_t *)vfs_fsprivate(mp);
    AssertReturn(pMount, EINVAL);

    /* Check case when vnode does not exist yet */
    if (ASMAtomicCmpXchgU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZING, VBOXVFS_OBJECT_UNINITIALIZED))
    {
        PDEBUG("Create new root vnode");

        /* Allocate empty SHFLSTRING to indicate path to root vnode within Shared Folder */
        char        szEmpty[1];
        SHFLSTRING *pSFVnodePath;

        pSFVnodePath = vboxvfs_construct_shflstring((char *)szEmpty, 0);
        if (pSFVnodePath)
        {
            int rc2;
            rc2 = vboxvfs_create_vnode_internal(mp, VDIR, NULL, TRUE, pSFVnodePath, &pMount->pRootVnode);
            if (rc2 != 0)
            {
                RTMemFree(pSFVnodePath);
                rc = ENOTSUP;
            }
        }
        else
            rc = ENOMEM;

        /* Notify other threads about result */
        if (rc == 0)
            ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INITIALIZED);
        else
            ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_INVALID);
    }
    else
    {
        /* Check case if we are currently creating vnode. Wait while other thread to finish allocation. */
        uint8_t fRootVnodeState = VBOXVFS_OBJECT_UNINITIALIZED;
        while (fRootVnodeState != VBOXVFS_OBJECT_INITIALIZED
            && fRootVnodeState != VBOXVFS_OBJECT_INVALID)
        {
            /* @todo: Currently, we are burning CPU cycles while waiting. This is for a short
             * time but we should relax here! */
            fRootVnodeState = ASMAtomicReadU8(&pMount->fRootVnodeState);

        }

        /* Check if the other thread initialized root vnode and it is ready to be returned */
        if (fRootVnodeState == VBOXVFS_OBJECT_INITIALIZED)
        {
            /* Take care about iocount */
            vid = vnode_vid(pMount->pRootVnode);
            rc = vnode_getwithvid(pMount->pRootVnode, vid);
        }
        else
        {
            /* Other thread reported initialization failure.
             * Set vnode state VBOXVFS_OBJECT_UNINITIALIZED in order to try recreate root
             * vnode in other attempt */
            ASMAtomicWriteU8(&pMount->fRootVnodeState, VBOXVFS_OBJECT_UNINITIALIZED);
        }

    }

    /* Only return vnode if we got success */
    if (rc == 0)
    {
        PDEBUG("Root vnode can be returned");
        *ppVnode = pMount->pRootVnode;
    }
    else
        PDEBUG("Root vnode cannot be returned: 0x%X", rc);

    return rc;
}
Exemple #17
0
__private_extern__ int
nget_9p(mount_9p *nmp, fid_9p fid, qid_9p qid, vnode_t dvp, vnode_t *vpp, struct componentname *cnp, vfs_context_t ctx)
{
#pragma unused(ctx)
	struct vnode_fsparam fsp;
	struct hnode_9p *nhp;
	node_9p *np;
	uint32_t vid;
	int e, i;

	TRACE();
	nhp = HASH9P(nmp, qid.path);
loop:
	lck_mtx_lock(nmp->nodelck);
	LIST_FOREACH (np, nhp, next) {
		if(np->dir.qid.path != qid.path)
			continue;
		if (ISSET(np->flags, NODE_INIT)) {
			SET(np->flags, NODE_WAITINIT);
			msleep(np, nmp->nodelck, PINOD|PDROP, "nget_9p_init", NULL);
			goto loop;
		}
		if (ISSET(np->flags, NODE_RECL)) {
			SET(np->flags, NODE_WAITRECL);
			msleep(np, nmp->nodelck, PINOD|PDROP, "nget_9p_reclaim", NULL);
			goto loop;
		}
		vid = vnode_vid(np->vp);
		lck_mtx_unlock(nmp->nodelck);
		if (vnode_getwithvid(np->vp, vid))
			goto loop;
		
		nlock_9p(np, NODE_LCK_EXCLUSIVE);
		if (dvp && cnp && ISSET(cnp->cn_flags, MAKEENTRY) && np->dir.qid.vers!=0) {
			// DEBUG("caching %s", np->dir->name);
			cache_enter(dvp, np->vp, cnp);
		} else {
			// DEBUG("not in cache qid=%d %s", qid.vers, np->dir->name);
		}

		*vpp = np->vp;
		return 0;
	}
	
	if (fid == NOFID)
		return EFAULT;

	np = malloc_9p(sizeof(*np));
	if (np == NULL) {
err0:
		lck_mtx_unlock(nmp->nodelck);
		return ENOMEM;
	}
	np->lck = lck_rw_alloc_init(lck_grp_9p, LCK_ATTR_NULL);
	if (np->lck == NULL) {
		free_9p(np);
		goto err0;
	}

	np->nmp = nmp;
	np->fid = fid;
	np->dir.qid = qid;
	for (i=0; i<3; i++)
		np->openfid[i].fid = NOFID;

	SET(np->flags, NODE_INIT);
	LIST_INSERT_HEAD(nhp, np, next);
	nlock_9p(np, NODE_LCK_EXCLUSIVE);
	lck_mtx_unlock(nmp->nodelck);

	if ((e=ngetdir_9p(np))) {
err1:
		nunlock_9p(np);
		lck_mtx_lock(nmp->nodelck);
		LIST_REMOVE(np, next);
		CLR(np->flags, NODE_INIT);
		if (ISSET(np->flags, NODE_WAITINIT)) {
			CLR(np->flags, NODE_WAITINIT);
			wakeup(np);
		}
		lck_mtx_unlock(nmp->nodelck);
		lck_rw_free(np->lck, lck_grp_9p);
		free_9p(np);
		return e;
	}

	fsp.vnfs_mp			= nmp->mp;
	fsp.vnfs_str		= fsname;
	fsp.vnfs_dvp		= dvp;
	fsp.vnfs_fsnode		= np;
	fsp.vnfs_vops		= vnode_op_9p;
	fsp.vnfs_markroot	= dvp==NULL? TRUE: FALSE;
	fsp.vnfs_marksystem	= FALSE;
	fsp.vnfs_filesize	= np->dir.length;
	fsp.vnfs_cnp		= cnp;
	fsp.vnfs_flags		= VNFS_ADDFSREF;
	dirvtype_9p(&np->dir, ISSET(nmp->flags, F_DOTU), &fsp.vnfs_vtype, &fsp.vnfs_rdev);
	if (!dvp || !cnp || !ISSET(cnp->cn_flags, MAKEENTRY) || qid.vers==0)
		SET(fsp.vnfs_flags, VNFS_NOCACHE);

	if ((e=vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &fsp, &np->vp)))
		goto err1;

	vnode_settag(np->vp, VT_OTHER);
	lck_mtx_lock(nmp->nodelck);
	CLR(np->flags, NODE_INIT);
	if (ISSET(np->flags, NODE_WAITINIT)) {
		CLR(np->flags, NODE_WAITINIT);
		wakeup(np);
	}
	lck_mtx_unlock(nmp->nodelck);
	*vpp = np->vp;

	return 0;
}
Exemple #18
0
static int
vdev_file_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
    uint64_t *ashift)
{
#if _KERNEL
	static vattr_t vattr;
#endif
	vdev_file_t *vf;
	struct vnode *vp;
	int error = 0;
    struct vnode *rootdir;

    dprintf("vdev_file_open %p\n", vd->vdev_tsd);

	/* Rotational optimizations only make sense on block devices */
	vd->vdev_nonrot = B_TRUE;

	/*
	 * We must have a pathname, and it must be absolute.
	 */
	if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
		vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
		return (SET_ERROR(EINVAL));
	}

	/*
	 * Reopen the device if it's not currently open.  Otherwise,
	 * just update the physical size of the device.
	 */
#ifdef _KERNEL
	if (vd->vdev_tsd != NULL) {
		ASSERT(vd->vdev_reopening);
		vf = vd->vdev_tsd;
        vnode_getwithvid(vf->vf_vnode, vf->vf_vid);
        dprintf("skip to open\n");
		goto skip_open;
	}
#endif

	vf = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_file_t), KM_SLEEP);

	/*
	 * We always open the files from the root of the global zone, even if
	 * we're in a local zone.  If the user has gotten to this point, the
	 * administrator has already decided that the pool should be available
	 * to local zone users, so the underlying devices should be as well.
	 */
	ASSERT(vd->vdev_path != NULL && vd->vdev_path[0] == '/');

    /*
      vn_openat(char *pnamep,
      enum uio_seg seg,
      int filemode,
      int createmode,
      struct vnode **vpp,
      enum create crwhy,
      mode_t umask,
      struct vnode *startvp)
      extern int vn_openat(char *pnamep, enum uio_seg seg, int filemode,
      int createmode, struct vnode **vpp, enum create crwhy,
      mode_t umask, struct vnode *startvp);
    */

    rootdir = getrootdir();

    error = vn_openat(vd->vdev_path + 1,
                      UIO_SYSSPACE,
                      spa_mode(vd->vdev_spa) | FOFFMAX,
                      0,
                      &vp,
                      0,
                      0,
                      rootdir
                      );

	if (error) {
		vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
		return (error);
	}

	vf->vf_vnode = vp;
#ifdef _KERNEL
    vf->vf_vid = vnode_vid(vp);
    dprintf("assigning vid %d\n", vf->vf_vid);

	/*
	 * Make sure it's a regular file.
	 */
	if (!vnode_isreg(vp)) {
        vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
        VN_RELE(vf->vf_vnode);
		return (SET_ERROR(ENODEV));
	}
#endif

#if _KERNEL
skip_open:
	/*
	 * Determine the physical size of the file.
	 */
	vattr.va_mask = AT_SIZE;
    vn_lock(vf->vf_vnode, LK_SHARED | LK_RETRY);
	error = VOP_GETATTR(vf->vf_vnode, &vattr, 0, kcred, NULL);
    VN_UNLOCK(vf->vf_vnode);
#endif
	if (error) {
		vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
        VN_RELE(vf->vf_vnode);
		return (error);
	}

#ifdef _KERNEL
	*max_psize = *psize = vattr.va_size;
#else
    /* userland's vn_open() will get the device size for us, so we can
     * just look it up - there is argument for a userland VOP_GETATTR to make
     * this function cleaner. */
	*max_psize = *psize = vp->v_size;
#endif
    *ashift = SPA_MINBLOCKSHIFT;
    VN_RELE(vf->vf_vnode);

	return (0);
}