Esempio n. 1
0
struct chan *fdtochan(struct fgrp *f, int fd, int mode, int chkmnt, int iref)
{
	
	struct chan *c;

	c = 0;

	spin_lock(&f->lock);
	if (f->closed) {
		spin_unlock(&f->lock);
		error("File group closed");
	}
	if (fd < 0 || f->maxfd < fd || (c = f->fd[fd]) == 0) {
		spin_unlock(&f->lock);
		set_errno(EBADF);
		error("Bad FD %d\n", fd);
	}
	if (iref)
		chan_incref(c);
	spin_unlock(&f->lock);

	if (chkmnt && (c->flag & CMSG)) {
		if (iref)
			cclose(c);
		error(Ebadusefd);
	}

	if (mode < 0 || c->mode == ORDWR) {
		return c;
	}

	if ((mode & OTRUNC) && IS_RDONLY(c->mode)) {
		if (iref)
			cclose(c);
		error(Ebadusefd);
	}

	/* TODO: this is probably wrong.  if you get this from a dev, in the dev's
	 * open, you are probably saving mode directly, without passing it through
	 * openmode. */
	if ((mode & ~OTRUNC) != c->mode) {
		warn("Trunc mode issue: mode %o, mode minus trunc %o, chan mode %o\n",
			 mode, mode & ~OTRUNC, c->mode);
		if (iref)
			cclose(c);
		error(Ebadusefd);
	}

	return c;
}
Esempio n. 2
0
/* Adds a tap with the file/qid of the underlying device for the requested FD.
 * The FD must be a chan, and the device must support the filter requested.
 *
 * Returns -1 or some other device-specific non-zero number on failure, 0 on
 * success. */
int add_fd_tap(struct proc *p, struct fd_tap_req *tap_req)
{
	struct fd_table *fdt = &p->open_files;
	struct fd_tap *tap;
	int ret = 0;
	struct chan *chan;
	int fd = tap_req->fd;

	if (fd < 0) {
		set_errno(EBADF);
		return -1;
	}
	tap = kzmalloc(sizeof(struct fd_tap), MEM_WAIT);
	tap->proc = p;
	tap->fd = fd;
	tap->filter = tap_req->filter;
	tap->ev_q = tap_req->ev_q;
	tap->ev_id = tap_req->ev_id;
	tap->data = tap_req->data;

	spin_lock(&fdt->lock);
	if (fd >= fdt->max_fdset) {
		set_errno(ENFILE);
		goto out_with_lock;
	}
	if (!GET_BITMASK_BIT(fdt->open_fds->fds_bits, fd)) {
		set_errno(EBADF);
		goto out_with_lock;
	}
	if (!fdt->fd[fd].fd_chan) {
		set_error(EINVAL, "Can't tap a VFS file");
		goto out_with_lock;
	}
	chan = fdt->fd[fd].fd_chan;
	if (fdt->fd[fd].fd_tap) {
		set_error(EBUSY, "FD %d already has a tap", fd);
		goto out_with_lock;
	}
	if (!devtab[chan->type].tapfd) {
		set_error(ENOSYS, "Device %s does not handle taps",
				  devtab[chan->type].name);
		goto out_with_lock;
	}
	/* need to keep chan alive for our call to the device.  someone else
	 * could come in and close the FD and the chan, once we unlock */
	chan_incref(chan);
	tap->chan = chan;
	/* One for the FD table, one for us to keep the removal of *this* tap from
	 * happening until we've attempted to register with the device. */
	kref_init(&tap->kref, tap_full_release, 2);
	fdt->fd[fd].fd_tap = tap;
	/* As soon as we unlock, another thread can come in and remove our old tap
	 * from the table and decref it.  Our ref keeps us from removing it yet,
	 * as well as keeps the memory safe.  However, a new tap can be installed
	 * and registered with the device before we even attempt to register.  The
	 * devices should be able to handle multiple, distinct taps, even if they
	 * happen to have the same {proc, fd} tuple. */
	spin_unlock(&fdt->lock);
	/* For refcnting fans, the tap ref is weak/uncounted.  We'll protect the
	 * memory and call the device when tap is being released. */
	ret = devtab[chan->type].tapfd(chan, tap, FDTAP_CMD_ADD);
	if (ret) {
		/* we failed, so we need to make sure *our* tap is removed.  We haven't
		 * decreffed, so we know our tap pointer is unique. */
		spin_lock(&fdt->lock);
		if (fdt->fd[fd].fd_tap == tap) {
			fdt->fd[fd].fd_tap = 0;
			/* normally we can't decref a tap while holding a lock, but we
			 * know we have another reference so this won't trigger a release */
			kref_put(&tap->kref);
		}
		spin_unlock(&fdt->lock);
		/* Regardless of whether someone else removed it or not, *we* are the
		 * only ones that know that registration failed and that we shouldn't
		 * remove it.  Since we still hold a ref, we can change the release
		 * method to skip the device dereg. */
		tap->kref.release = tap_min_release;
	}
	kref_put(&tap->kref);
	return ret;
out_with_lock:
	spin_unlock(&fdt->lock);
	kfree(tap);
	return -1;
}