static bool
fifo_open(struct fifo_data *fd, GError **error)
{
	if (!fifo_check(fd, error))
		return false;

	fd->input = open_cloexec(fd->path, O_RDONLY|O_NONBLOCK, 0);
	if (fd->input < 0) {
		g_set_error(error, fifo_output_quark(), errno,
			    "Could not open FIFO \"%s\" for reading: %s",
			    fd->path, strerror(errno));
		fifo_close(fd);
		return false;
	}

	fd->output = open_cloexec(fd->path, O_WRONLY|O_NONBLOCK, 0);
	if (fd->output < 0) {
		g_set_error(error, fifo_output_quark(), errno,
			    "Could not open FIFO \"%s\" for writing: %s",
			    fd->path, strerror(errno));
		fifo_close(fd);
		return false;
	}

	return true;
}
static bool
recorder_output_open(void *data, struct audio_format *audio_format,
		     GError **error_r)
{
	struct recorder_output *recorder = data;
	bool success;

	/* create the output file */

	recorder->fd = open_cloexec(recorder->path, O_CREAT|O_WRONLY|O_TRUNC,
				    0666);
	if (recorder->fd < 0) {
		g_set_error(error_r, recorder_output_quark(), 0,
			    "Failed to create '%s': %s",
			    recorder->path, g_strerror(errno));
		return false;
	}

	/* open the encoder */

	success = encoder_open(recorder->encoder, audio_format, error_r);
	if (!success) {
		close(recorder->fd);
		unlink(recorder->path);
		return false;
	}

	return true;
}
Ejemplo n.º 3
0
static int
open_log_file(void)
{
	assert(out_filename != NULL);

	return open_cloexec(out_filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
}
Ejemplo n.º 4
0
static bool
solaris_output_open(struct audio_output *ao, struct audio_format *audio_format,
		    GError **error)
{
	struct solaris_output *so = (struct solaris_output *)ao;
	struct audio_info info;
	int ret, flags;

	/* support only 16 bit mono/stereo for now; nothing else has
	   been tested */
	audio_format->format = SAMPLE_FORMAT_S16;

	/* open the device in non-blocking mode */

	so->fd = open_cloexec(so->device, O_WRONLY|O_NONBLOCK, 0);
	if (so->fd < 0) {
		g_set_error(error, solaris_output_quark(), errno,
			    "Failed to open %s: %s",
			    so->device, g_strerror(errno));
		return false;
	}

	/* restore blocking mode */

	flags = fcntl(so->fd, F_GETFL);
	if (flags > 0 && (flags & O_NONBLOCK) != 0)
		fcntl(so->fd, F_SETFL, flags & ~O_NONBLOCK);

	/* configure the audio device */

	ret = ioctl(so->fd, AUDIO_GETINFO, &info);
	if (ret < 0) {
		g_set_error(error, solaris_output_quark(), errno,
			    "AUDIO_GETINFO failed: %s", g_strerror(errno));
		close(so->fd);
		return false;
	}

	info.play.sample_rate = audio_format->sample_rate;
	info.play.channels = audio_format->channels;
	info.play.precision = 16;
	info.play.encoding = AUDIO_ENCODING_LINEAR;

	ret = ioctl(so->fd, AUDIO_SETINFO, &info);
	if (ret < 0) {
		g_set_error(error, solaris_output_quark(), errno,
			    "AUDIO_SETINFO failed: %s", g_strerror(errno));
		close(so->fd);
		return false;
	}

	return true;
}
static struct input_stream *
input_file_open(const char *filename, GError **error_r)
{
	int fd, ret;
	struct stat st;
	struct file_input_stream *fis;

	if (!g_path_is_absolute(filename))
		return false;

	fd = open_cloexec(filename, O_RDONLY, 0);
	if (fd < 0) {
		if (errno != ENOENT && errno != ENOTDIR)
			g_set_error(error_r, file_quark(), errno,
				    "Failed to open \"%s\": %s",
				    filename, g_strerror(errno));
		return false;
	}

	ret = fstat(fd, &st);
	if (ret < 0) {
		g_set_error(error_r, file_quark(), errno,
			    "Failed to stat \"%s\": %s",
			    filename, g_strerror(errno));
		close(fd);
		return false;
	}

	if (!S_ISREG(st.st_mode)) {
		g_set_error(error_r, file_quark(), 0,
			    "Not a regular file: %s", filename);
		close(fd);
		return false;
	}

#ifdef POSIX_FADV_SEQUENTIAL
	posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif

	fis = g_new(struct file_input_stream, 1);
	input_stream_init(&fis->base, &input_plugin_file, filename);

	fis->base.size = st.st_size;
	fis->base.seekable = true;
	fis->base.ready = true;

	fis->fd = fd;

	return &fis->base;
}
Ejemplo n.º 6
0
static bool
mvp_output_open(struct audio_output *ao, struct audio_format *audio_format,
		GError **error)
{
	struct mvp_data *md = (struct mvp_data *)ao;
	long long int stc = 0;
	int mix[5] = { 0, 2, 7, 1, 0 };
	bool success;

	md->fd = open_cloexec("/dev/adec_pcm", O_RDWR | O_NONBLOCK, 0);
	if (md->fd < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Error opening /dev/adec_pcm: %s",
			    g_strerror(errno));
		return false;
	}
	if (ioctl(md->fd, MVP_SET_AUD_SRC, 1) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Error setting audio source: %s",
			    g_strerror(errno));
		return false;
	}
	if (ioctl(md->fd, MVP_SET_AUD_STREAMTYPE, 0) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Error setting audio streamtype: %s",
			    g_strerror(errno));
		return false;
	}
	if (ioctl(md->fd, MVP_SET_AUD_FORMAT, &mix) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Error setting audio format: %s",
			    g_strerror(errno));
		return false;
	}
	ioctl(md->fd, MVP_SET_AUD_STC, &stc);
	if (ioctl(md->fd, MVP_SET_AUD_BYPASS, 1) < 0) {
		g_set_error(error, mvp_output_quark(), errno,
			    "Error setting audio streamtype: %s",
			    g_strerror(errno));
		return false;
	}

	success = mvp_set_pcm_params(md, audio_format, error);
	if (!success)
		return false;

	md->audio_format = *audio_format;
	return true;
}
Ejemplo n.º 7
0
static bool
mvp_output_test_default_device(void)
{
	int fd;

	fd = open_cloexec("/dev/adec_pcm", O_WRONLY, 0);

	if (fd >= 0) {
		close(fd);
		return true;
	}

	g_warning("Error opening PCM device \"/dev/adec_pcm\": %s\n",
		  strerror(errno));

	return false;
}
Ejemplo n.º 8
0
int init_ctl_channel(const char *name, int verb)
{
	char buf[PATH_MAX];
	struct statfs st;
	int old_transport = 0;

        (void) verb;
        if (0) goto out; /* just to defeat gcc warnings */

	/* Before trying to open the control channel, make sure it
	 * isn't already open. */
	close_ctl_channel();

#ifdef HAVE_OPENAT
        if (relay_basedir_fd >= 0) {
                strncpy(buf, CTL_CHANNEL_NAME, PATH_MAX);
                control_channel = openat_cloexec(relay_basedir_fd,
						 CTL_CHANNEL_NAME, O_RDWR, 0);
                dbug(2, "Opened %s (%d)\n", CTL_CHANNEL_NAME, control_channel);

                /* NB: Extra real-id access check as below */
                if (faccessat(relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) != 0){
                        close(control_channel);
                        return -5;
                }
                if (control_channel >= 0)
                        goto out; /* It's OK to bypass the [f]access[at] check below,
                                     since this would only occur the *second* time 
                                     staprun tries this gig, or within unprivileged stapio. */
        }
        /* PR14245, NB: we fall through to /sys ... /proc searching,
           in case the relay_basedir_fd option wasn't given (i.e., for
           early in staprun), or if errors out for some reason. */
#endif

	if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) {
                /* PR14245: allow subsequent operations, and if
                   necessary, staprun->stapio forks, to reuse an fd for 
                   directory lookups (even if some parent directories have
                   perms 0700. */
#ifdef HAVE_OPENAT
                if (! sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s", name)) {
                        relay_basedir_fd = open (buf, O_DIRECTORY | O_RDONLY);
                        /* If this fails, we don't much care; the
                           negative return value will just keep us
                           looking up by name again next time. */
                        /* NB: we don't plan to close this fd, so that we can pass
                           it across staprun->stapio fork/execs. */
                }
#endif
		if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/%s", 
                                name, CTL_CHANNEL_NAME))
			return -1;
	} else {
		old_transport = 1;
		if (sprintf_chk(buf, "/proc/systemtap/%s/%s", name, CTL_CHANNEL_NAME))
			return -2;
	}

	control_channel = open_cloexec(buf, O_RDWR, 0);
	dbug(2, "Opened %s (%d)\n", buf, control_channel);

	/* NB: Even if open() succeeded with effective-UID permissions, we
	 * need the access() check to make sure real-UID permissions are also
	 * sufficient.  When we run under the setuid staprun, effective and
	 * real UID may not be the same.  Specifically, we want to prevent 
         * a local stapusr from trying to attach to a different stapusr's module.
	 *
	 * The access() is done *after* open() to avoid any TOCTOU-style race
	 * condition.  We believe it's probably safe either way, as the file
	 * we're trying to access connot be modified by a typical user, but
	 * better safe than sorry.
	 */
#ifdef HAVE_OPENAT
        if (control_channel >= 0 && relay_basedir_fd >= 0) {
                if (faccessat (relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) == 0)
                        goto out;
                /* else fall through */
        }
#endif
	if (control_channel >= 0 && access(buf, R_OK|W_OK) != 0) {
		close(control_channel);
		return -5;
	}

out:
	if (control_channel < 0) {
                err(_("Cannot attach to module %s control channel; not running?\n"),
                    name);
		return -3;
	}
	return old_transport;
}
Ejemplo n.º 9
0
/* Safely perform a change in directory.  We do this by calling
 * lstat() on the subdirectory, using chdir() to move into it, and
 * then lstat()ing ".".  We compare the results of the two stat calls
 * to see if they are consistent.  If not, we sound the alarm.
 *
 * If following_links() is true, we do follow symbolic links.
 */
static enum SafeChdirStatus
safely_chdir_lstat (const char *dest,
		    enum TraversalDirection direction,
		    struct stat *statbuf_dest,
		    enum ChdirSymlinkHandling symlink_follow_option,
		    bool *did_stat)
{
  struct stat statbuf_arrived;
  int rv, dotfd=-1;
  int saved_errno;		/* specific_dirname() changes errno. */
  bool rv_set = false;
  bool statflag = false;
  int tries = 0;
  enum WdSanityCheckFatality isfatal = RETRY_IF_SANITY_CHECK_FAILS;

  saved_errno = errno = 0;

  dotfd = open_cloexec (".", O_RDONLY
#if defined O_LARGEFILE
			|O_LARGEFILE
#endif
			);

  /* We jump back to here if wd_sanity_check()
   * recoverably triggers an alert.
   */
 retry:
  ++tries;

  if (dotfd >= 0)
    {
      /* Stat the directory we're going to. */
      set_stat_placeholders (statbuf_dest);
      if (0 == options.xstat (dest, statbuf_dest))
	{
	  statflag = true;

#ifdef S_ISLNK
	  /* symlink_follow_option might be set to SymlinkFollowOk, which
	   * would allow us to chdir() into a symbolic link.  This is
	   * only useful for the case where the directory we're
	   * chdir()ing into is the basename of a command line
	   * argument, for example where "foo/bar/baz" is specified on
	   * the command line.  When -P is in effect (the default),
	   * baz will not be followed if it is a symlink, but if bar
	   * is a symlink, it _should_ be followed.  Hence we need the
	   * ability to override the policy set by following_links().
	   */
	  if (!following_links () && S_ISLNK(statbuf_dest->st_mode))
	    {
	      /* We're not supposed to be following links, but this is
	       * a link.  Check symlink_follow_option to see if we should
	       * make a special exception.
	       */
	      if (symlink_follow_option == SymlinkFollowOk)
		{
		  /* We need to re-stat() the file so that the
		   * sanity check can pass.
		   */
		  if (0 != stat (dest, statbuf_dest))
		    {
		      rv = SafeChdirFailNonexistent;
		      rv_set = true;
		      saved_errno = errno;
		      goto fail;
		    }
		  statflag = true;
		}
	      else
		{
		  /* Not following symlinks, so the attempt to
		   * chdir() into a symlink should be prevented.
		   */
		  rv = SafeChdirFailSymlink;
		  rv_set = true;
		  saved_errno = 0;	/* silence the error message */
		  goto fail;
		}
	    }
#endif
#ifdef S_ISDIR
	  /* Although the immediately following chdir() would detect
	   * the fact that this is not a directory for us, this would
	   * result in an extra system call that fails.  Anybody
	   * examining the system-call trace should ideally not be
	   * concerned that something is actually failing.
	   */
	  if (!S_ISDIR(statbuf_dest->st_mode))
	    {
	      rv = SafeChdirFailNotDir;
	      rv_set = true;
	      saved_errno = 0;	/* silence the error message */
	      goto fail;
	    }
#endif

	  if (options.debug_options & DebugSearch)
	    fprintf (stderr, "safely_chdir(): chdir(\"%s\")\n", dest);

	  if (0 == chdir (dest))
	    {
	      /* check we ended up where we wanted to go */
	      bool changed = false;
	      if (!wd_sanity_check (".", program_name, ".",
				    statbuf_dest->st_dev,
				    statbuf_dest->st_ino,
				    &statbuf_arrived,
				    0, __LINE__, direction,
				    isfatal,
				    &changed))
		{
		  /* Only allow one failure. */
		  if (RETRY_IF_SANITY_CHECK_FAILS == isfatal)
		    {
		      if (0 == fchdir (dotfd))
			{
			  isfatal = FATAL_IF_SANITY_CHECK_FAILS;
			  goto retry;
			}
		      else
			{
			  /* Failed to return to original directory,
			   * but we know that the current working
			   * directory is not the one that we intend
			   * to be in.  Since fchdir() failed, we
			   * can't recover from this and so this error
			   * is fatal.
			   */
			  error (EXIT_FAILURE, errno,
				 _("failed to return to parent directory"));
			}
		    }
		  else
		    {
		      /* XXX: not sure what to use as an excuse here. */
		      rv = SafeChdirFailNonexistent;
		      rv_set = true;
		      saved_errno = 0;
		      goto fail;
		    }
		}

	      close (dotfd);
	      return SafeChdirOK;
	    }
	  else
	    {
	      saved_errno = errno;
	      if (ENOENT == saved_errno)
		{
		  rv = SafeChdirFailNonexistent;
		  rv_set = true;
		  if (options.ignore_readdir_race)
		    errno = 0;	/* don't issue err msg */
		}
	      else if (ENOTDIR == saved_errno)
		{
		  /* This can happen if the we stat a directory,
		   * and then file system activity changes it into
		   * a non-directory.
		   */
		  saved_errno = 0;	/* don't issue err msg */
		  rv = SafeChdirFailNotDir;
		  rv_set = true;
		}
	      else
		{
		  rv = SafeChdirFailChdirFailed;
		  rv_set = true;
		}
	      goto fail;
	    }
	}
      else
	{
	  saved_errno = errno;
	  rv = SafeChdirFailStat;
	  rv_set = true;

	  if ( (ENOENT == saved_errno) || (0 == state.curdepth))
	    saved_errno = 0;	/* don't issue err msg */
	  goto fail;
	}
    }
  else
    {
      /* We do not have read permissions on "." */
      rv = SafeChdirFailWouldBeUnableToReturn;
      rv_set = true;
      goto fail;
    }

  /* This is the success path, so we clear errno.  The caller probably
   * won't be calling error() anyway.
   */
  saved_errno = 0;

  /* We use the same exit path for success or failure.
   * which has occurred is recorded in RV.
   */
 fail:
  /* We do not call error() as this would result in a duplicate error
   * message when the caller does the same thing.
   */
  if (saved_errno)
    errno = saved_errno;

  if (dotfd >= 0)
    {
      close (dotfd);
      dotfd = -1;
    }

  *did_stat = statflag;
  assert (rv_set);
  return rv;
}
Ejemplo n.º 10
0
static int
_slurmd_init(void)
{
	struct rlimit rlim;
	slurm_ctl_conf_t *cf;
	struct stat stat_buf;
	uint32_t cpu_cnt;

	/*
	 * Process commandline arguments first, since one option may be
	 * an alternate location for the slurm config file.
	 */
	_process_cmdline(*conf->argc, *conf->argv);

	/*
	 * Build nodes table like in slurmctld
	 * This is required by the topology stack
	 * Node tables setup must preceed _read_config() so that the
	 * proper hostname is set.
	 */
	slurm_conf_init(conf->conffile);
	init_node_conf();
	/* slurm_select_init() must be called before
	 * build_all_nodeline_info() to be called with proper argument. */
	if (slurm_select_init(1) != SLURM_SUCCESS )
		return SLURM_FAILURE;
	build_all_nodeline_info(true);
	build_all_frontend_info(true);

	/*
	 * Read global slurm config file, override necessary values from
	 * defaults and command line.
	 */
	_read_config();

	cpu_cnt = MAX(conf->conf_cpus, conf->block_map_size);

	if ((gres_plugin_init() != SLURM_SUCCESS) ||
	    (gres_plugin_node_config_load(cpu_cnt) != SLURM_SUCCESS))
		return SLURM_FAILURE;
	if (slurm_topo_init() != SLURM_SUCCESS)
		return SLURM_FAILURE;

	/*
	 * Get and set slurmd topology information
	 * Build node hash table first to speed up the topo build
	 */
	rehash_node();
	slurm_topo_build_config();
	_set_topo_info();

	/*
	 * Check for cpu frequency set capabilities on this node
	 */
	cpu_freq_init(conf);

	_print_conf();

	if (slurm_proctrack_init() != SLURM_SUCCESS)
		return SLURM_FAILURE;
	if (slurmd_task_init() != SLURM_SUCCESS)
		return SLURM_FAILURE;
	if (slurm_auth_init(NULL) != SLURM_SUCCESS)
		return SLURM_FAILURE;
	if (spank_slurmd_init() < 0)
		return SLURM_FAILURE;

	if (getrlimit(RLIMIT_CPU, &rlim) == 0) {
		rlim.rlim_cur = rlim.rlim_max;
		setrlimit(RLIMIT_CPU, &rlim);
		if (rlim.rlim_max != RLIM_INFINITY) {
			error("Slurmd process CPU time limit is %d seconds",
			      (int) rlim.rlim_max);
		}
	}

	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
		rlim.rlim_cur = rlim.rlim_max;
		setrlimit(RLIMIT_NOFILE, &rlim);
	}
#ifndef NDEBUG
	if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
		rlim.rlim_cur = rlim.rlim_max;
		setrlimit(RLIMIT_CORE, &rlim);
	}
#endif /* !NDEBUG */

	/*
	 * Create a context for verifying slurm job credentials
	 */
	if (!(conf->vctx = slurm_cred_verifier_ctx_create(conf->pubkey)))
		return SLURM_FAILURE;
	if (!strcmp(conf->select_type, "select/serial")) {
		/* Only cache credential for 5 seconds with select/serial
		 * for shorter cache searches and higher throughput */
		slurm_cred_ctx_set(conf->vctx, SLURM_CRED_OPT_EXPIRY_WINDOW, 5);
	}

	/*
	 * Create slurmd spool directory if necessary.
	 */
	if (_set_slurmd_spooldir() < 0) {
		error("Unable to initialize slurmd spooldir");
		return SLURM_FAILURE;
	}

	if (conf->cleanstart) {
		/*
		 * Need to kill any running slurmd's here
		 */
		_kill_old_slurmd();

		stepd_cleanup_sockets(conf->spooldir, conf->node_name);
		_stepd_cleanup_batch_dirs(conf->spooldir, conf->node_name);
	}

	if (conf->daemonize) {
		bool success = false;

		if (conf->logfile && (conf->logfile[0] == '/')) {
			char *slash_ptr, *work_dir;
			work_dir = xstrdup(conf->logfile);
			slash_ptr = strrchr(work_dir, '/');
			if (slash_ptr == work_dir)
				work_dir[1] = '\0';
			else
				slash_ptr[0] = '\0';
			if ((access(work_dir, W_OK) != 0) ||
			    (chdir(work_dir) < 0)) {
				error("Unable to chdir to %s", work_dir);
			} else
				success = true;
			xfree(work_dir);
		}

		if (!success) {
			if ((access(conf->spooldir, W_OK) != 0) ||
			    (chdir(conf->spooldir) < 0)) {
				error("Unable to chdir to %s", conf->spooldir);
			} else
				success = true;
		}

		if (!success) {
			if ((access("/var/tmp", W_OK) != 0) ||
			    (chdir("/var/tmp") < 0)) {
				error("chdir(/var/tmp): %m");
				return SLURM_FAILURE;
			} else
				info("chdir to /var/tmp");
		}
	}

	/*
	 * Cache the group access list
	 */
	cf = slurm_conf_lock();
	if (cf->group_info & GROUP_CACHE)
		init_gids_cache(1);
	else
		init_gids_cache(0);
	slurm_conf_unlock();

	if ((devnull = open_cloexec("/dev/null", O_RDWR)) < 0) {
		error("Unable to open /dev/null: %m");
		return SLURM_FAILURE;
	}

	/* make sure we have slurmstepd installed */
	if (stat(conf->stepd_loc, &stat_buf))
		fatal("Unable to find slurmstepd file at %s", conf->stepd_loc);
	if (!S_ISREG(stat_buf.st_mode))
		fatal("slurmstepd not a file at %s", conf->stepd_loc);

	return SLURM_SUCCESS;
}
 static std::streambuf* open_file(const char* path) {
   int fd = open_cloexec(path, O_RDONLY);
   return new STDIO_FILEBUF_TYPE(fd, std::ios::in);
 }
static struct input_stream *
input_file_open(const char *filename,
		GMutex *mutex, GCond *cond,
		GError **error_r)
{
	int fd, ret;
	struct stat st;
	struct file_input_stream *fis;

	if (!g_path_is_absolute(filename))
		return NULL;

	fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0);
        if (fd < 0)  {
		// the filename doesn't exist	
		// try and open uri as if it were a track inside a container
		char* pathname = NULL;
		unsigned tnum;

		pathname = g_strdup(filename);
		remove_suffix(pathname,'/');

		tnum = cue_vtrack_tnum(filename);
		if ( tnum == 0 )
			tnum=1; // use filename from first track of cue file if no track found 
			
                if( g_str_has_suffix(pathname,".cue"))
                    pathname=cue_locate_audio_container_file(pathname,tnum) ;

		fd = open_cloexec(pathname, O_RDONLY|O_BINARY, 0);
		g_free(pathname);

		if (fd < 0) {
			if (errno != ENOENT && errno != ENOTDIR)
				g_set_error(error_r, file_quark(), errno,
						"Failed to open \"%s\": %s",
						filename, g_strerror(errno));
			return NULL;
		}
	}

	ret = fstat(fd, &st);
	if (ret < 0) {
		g_set_error(error_r, file_quark(), errno,
			    "Failed to stat \"%s\": %s",
			    filename, g_strerror(errno));
		close(fd);
		return NULL;
	}

	if (!S_ISREG(st.st_mode)) {
		g_set_error(error_r, file_quark(), 0,
			    "Not a regular file: %s", filename);
		close(fd);
		return NULL;
	}

#ifdef POSIX_FADV_SEQUENTIAL
	posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif

	fis = g_new(struct file_input_stream, 1);
	input_stream_init(&fis->base, &input_plugin_file, filename,
			  mutex, cond);

	fis->base.size = st.st_size;
	fis->base.seekable = true;
	fis->base.ready = true;

	fis->fd = fd;

	return &fis->base;
}