Beispiel #1
0
/* Open the file NAME, and return a new store in STORE, which refers to the
   storage underlying it.  CLASSES is used to select classes specified by the
   provider; if it is 0, STORE_STD_CLASSES is used.  FLAGS is set with
   store_set_flags.  A reference to the open file is created (but may be
   destroyed with store_close_source).  */
error_t
store_open (const char *name, int flags,
	    const struct store_class *const *classes,
	    struct store **store)
{
  error_t err;
  int open_flags = (flags & STORE_HARD_READONLY) ? O_RDONLY : O_RDWR;
  file_t node = file_name_lookup (name, open_flags, 0);

  if (node == MACH_PORT_NULL && !(flags & STORE_HARD_READONLY)
      && (errno == EACCES || errno == EROFS))
    {
      flags |= STORE_HARD_READONLY;
      node = file_name_lookup (name, O_RDONLY, 0);
    }

  if (node == MACH_PORT_NULL)
    return errno;

  err = store_create (node, flags, classes, store);
  if (err)
    {
      if (! (flags & STORE_NO_FILEIO))
	/* Try making a store that does file io to NODE.  */
	err = store_file_create (node, flags, store);
      if (err)
	mach_port_deallocate (mach_task_self (), node);
    }

  return err;
}
Beispiel #2
0
int
ethernet_open (struct device *dev)
{
  error_t err;
  device_t master_device;
  struct ether_device *edev = (struct ether_device *) dev->priv;

  assert (edev->ether_port == MACH_PORT_NULL);

  err = ports_create_port (etherreadclass, etherport_bucket,
			   sizeof (struct port_info), &edev->readpt);
  assert_perror (err);
  edev->readptname = ports_get_right (edev->readpt);
  mach_port_insert_right (mach_task_self (), edev->readptname, edev->readptname,
			  MACH_MSG_TYPE_MAKE_SEND);

  mach_port_set_qlimit (mach_task_self (), edev->readptname, MACH_PORT_QLIMIT_MAX);

  master_device = file_name_lookup (dev->name, O_READ | O_WRITE, 0);
  if (master_device != MACH_PORT_NULL)
    {
      /* The device name here is the path of a device file.  */
      err = device_open (master_device, D_WRITE | D_READ, "eth", &edev->ether_port);
      mach_port_deallocate (mach_task_self (), master_device);
      if (err)
	error (2, err, "device_open on %s", dev->name);

      err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt),
			       MACH_MSG_TYPE_MAKE_SEND, 0,
			       bpf_ether_filter, bpf_ether_filter_len);
      if (err)
	error (2, err, "device_set_filter on %s", dev->name);
    }
  else
    {
      /* No, perhaps a Mach device?  */
      int file_errno = errno;
      err = get_privileged_ports (0, &master_device);
      if (err)
	{
	  error (0, file_errno, "file_name_lookup %s", dev->name);
	  error (2, err, "and cannot get device master port");
	}
      err = device_open (master_device, D_WRITE | D_READ, dev->name, &edev->ether_port);
      mach_port_deallocate (mach_task_self (), master_device);
      if (err)
	{
	  error (0, file_errno, "file_name_lookup %s", dev->name);
	  error (2, err, "device_open(%s)", dev->name);
	}

      err = device_set_filter (edev->ether_port, ports_get_right (edev->readpt),
			       MACH_MSG_TYPE_MAKE_SEND, 0,
			       ether_filter, ether_filter_len);
      if (err)
	error (2, err, "device_set_filter on %s", dev->name);
    }

  return 0;
}
Beispiel #3
0
void
arrange_shutdown_notification ()
{
    error_t err;
    mach_port_t initport, notify;
    struct port_info *pi;

    shutdown_notify_class = ports_create_class (0, 0);

    signal (SIGTERM, sigterm_handler);

    /* Arrange to get notified when the system goes down,
       but if we fail for some reason, just silently give up.  No big deal. */

    err = ports_create_port (shutdown_notify_class, pfinet_bucket,
                             sizeof (struct port_info), &pi);
    if (err)
        return;

    initport = file_name_lookup (_SERVERS_STARTUP, 0, 0);
    if (initport == MACH_PORT_NULL)
        return;

    notify = ports_get_send_right (pi);
    ports_port_deref (pi);
    startup_request_notification (initport, notify,
                                  MACH_MSG_TYPE_MAKE_SEND,
                                  program_invocation_short_name);
    mach_port_deallocate (mach_task_self (), notify);
    mach_port_deallocate (mach_task_self (), initport);
}
Beispiel #4
0
  error_t parse_opt (int key, char *arg, struct argp_state *state)
    {
      switch (key)
	{
	case 'f': use_file_io = 1; break;
	case 'b': block_size = atoi (arg); break;

	case ARGP_KEY_ARG:
	  if (! store)
	    {
	      error_t err;
	      file_t source = file_name_lookup (arg, O_READ, 0);
	      if (errno)
		error (2, errno, "%s", arg);
	      if (use_file_io)
		if (block_size)
		  {
		    struct stat stat;
		    err = io_stat (source, &stat);
		    if (! err)
		      {
			struct store_run run = {0, stat.st_size / block_size};
			err = _store_file_create (source, 0, block_size, &run, 1,
						  &store);
		      }
		  }
		else
		  err = store_file_create (source, 0, &store);
	      else
		err = store_create (source, 0, 0, &store);
	      if (err)
		error (3, err, "%s", arg);
	    }
	  else if (addr < 0)
	    addr = atoll (arg);
	  else
	    {
	      dump (addr, atoi (arg));
	      dumped = 1;
	      addr = -1;
	    }
	  break;

	case ARGP_KEY_END:
	  if (!store)
	    argp_usage (state);

	  if (addr >= 0)
	    dump (addr, -1);
	  else if (! dumped)
	    dump (0, -1);
	  break;

	case ARGP_KEY_NO_ARGS:
	default:
	  return ARGP_ERR_UNKNOWN;
	}
      return 0;
    }
Beispiel #5
0
/* Set standard error to ARGV[0].  Optionally, ARGV[1] may specify the
   file access mode (see str2flags).  The default is O_RDONLY */
error_t
cmd_stderr (pid_t pid, mach_port_t msgport, int argc, char *argv[])
{
  error_t err;
  int flags = str2flags (argc > 2 ? argv[2] : "w");
  file_t file = file_name_lookup (argv[0], flags, 0666);
  if (file == MACH_PORT_NULL)
    return errno;
  err = do_setfd (pid, msgport, STDERR_FILENO, file);
  if (err)
    mach_port_deallocate (mach_task_self (), file);
  return err;
}
Beispiel #6
0
int
main(int argc, char *argv[])
{
  int value=0;
  mach_port_t random_server_port;

  random_server_port = file_name_lookup ("/tmp/trans", 0, 0);
  printf ("random_server_port [%u]\n", (unsigned int)random_server_port);
  S_get_random (random_server_port, &value);
  printf ("get_random: [%d]\n", value);

  return 0;
}
Beispiel #7
0
error_t
setup_hurd_target (void)
{
  char *name = (char *) setup_argument;
  mach_port_t request;
  mach_msg_type_name_t requestType;

  request = file_name_lookup (name, 0, 0);
  if (! MACH_PORT_VALID (request))
    return errno;
  requestType = MACH_MSG_TYPE_COPY_SEND;

  return setup (request, requestType);
}
Beispiel #8
0
static error_t
get_swapinfo (default_pager_info_t *info)
{
  mach_port_t defpager;
  error_t err;

  defpager = file_name_lookup (_SERVERS_DEFPAGER, O_READ, 0);
  if (defpager == MACH_PORT_NULL)
    return errno;

  err = default_pager_info (defpager, info);
  mach_port_deallocate (mach_task_self (), defpager);

  return err;
}
Beispiel #9
0
Datei: user.c Projekt: ctos/bpi
int main()
{
	mach_port_t port;

	port = file_name_lookup("/tmp/cache", 0, 0);
	Cache_send_message(port, "hello,world.\n");
	
	string_t message;
	Cache_get_message(port, message);

	printf("%s\n", message);


	return 0;
}
Beispiel #10
0
void
pfinet_bind (int portclass, const char *name)
{
    struct trivfs_control *cntl;
    error_t err = 0;
    mach_port_t right;
    file_t file = file_name_lookup (name, O_CREAT|O_NOTRANS, 0666);

    if (file == MACH_PORT_NULL)
        err = errno;

    if (! err) {
        if (pfinet_protid_portclasses[portclass] != MACH_PORT_NULL)
            error (1, 0, "Cannot bind one protocol to multiple nodes.\n");

#ifdef CONFIG_IPV6
        if (portclass == PORTCLASS_INET6)
            pfinet_activate_ipv6 ();
#endif
        //mark
        err = trivfs_add_protid_port_class (&pfinet_protid_portclasses[portclass]);
        if (err)
            error (1, 0, "error creating control port class");

        err = trivfs_add_control_port_class (&pfinet_cntl_portclasses[portclass]);
        if (err)
            error (1, 0, "error creating control port class");

        err = trivfs_create_control (file, pfinet_cntl_portclasses[portclass],
                                     pfinet_bucket,
                                     pfinet_protid_portclasses[portclass],
                                     pfinet_bucket, &cntl);
    }

    if (! err)
    {
        right = ports_get_send_right (cntl);
        err = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
                                   0, 0, 0, right, MACH_MSG_TYPE_COPY_SEND);
        mach_port_deallocate (mach_task_self (), right);
    }

    if (err)
        error (1, err, "%s", name);

    ports_port_deref (cntl);

}
Beispiel #11
0
int
main (int argc, char **argv)
{
  error_t err;
  mach_port_t pflocal;
  mach_port_t bootstrap;
  char buf[512];
  const struct argp argp = { 0, 0, 0, doc };

  argp_parse (&argp, argc, argv, 0, 0, 0);

  control_class = ports_create_class (trivfs_clean_cntl, 0);
  node_class = ports_create_class (trivfs_clean_protid, 0);
  port_bucket = ports_create_bucket ();
  trivfs_protid_portclasses[0] = node_class;
  trivfs_cntl_portclasses[0] = control_class;

  task_get_bootstrap_port (mach_task_self (), &bootstrap);
  if (bootstrap == MACH_PORT_NULL)
    error(1, 0, "Must be started as a translator");

  /* Reply to our parent */
  err = trivfs_startup (bootstrap, 0, control_class, port_bucket,
			node_class, port_bucket, NULL);
  mach_port_deallocate (mach_task_self (), bootstrap);
  if (err)
    error(2, err, "Contacting parent");

  /* Try and connect to the pflocal server */
  sprintf (buf, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
  pflocal = file_name_lookup (buf, 0, 0);

  if (pflocal == MACH_PORT_NULL)
    address_port = MACH_PORT_NULL;
  else
    {
      err = socket_fabricate_address (pflocal, AF_LOCAL, &address_port);
      if (err)
	address_port = MACH_PORT_NULL;
      mach_port_deallocate (mach_task_self (), pflocal);
    }

  /* Launch. */
  ports_manage_port_operations_one_thread (port_bucket, demuxer, 0);
  return 0;
}
Beispiel #12
0
/* Assert the DTR if necessary.  Must be called with global lock held.  */
static void
wait_for_dtr (void)
{
  while (!assert_dtr)
    hurd_condition_wait (&hurdio_assert_dtr_condition, &global_lock);
  assert_dtr = 0;

  if (tty_arg == 0)
    ioport = termctl->underlying;
  else
    {
      /* Open the file in blocking mode, so that the carrier is
	 established as well.  */
      ioport = file_name_lookup (tty_arg, O_READ|O_WRITE, 0);
      if (ioport == MACH_PORT_NULL)
	{
	  report_carrier_error (errno);
	  return;
	}
    }


  error_t err;
  struct termios state = termstate;

  /* Assume that we have a full blown terminal initially.  */
  tioc_caps = ~0;

  /* Set terminal in raw mode etc.  */
  err = hurdio_set_bits (&state);
  if (err)
    report_carrier_error (err);
  else
    {
      termstate = state;

      /* Signal that we have a carrier.  */
      report_carrier_on ();

      /* Signal that the writer thread should resume its work.  */
      condition_broadcast (&hurdio_writer_condition);
    }
}
Beispiel #13
0
/*Lookup `name` under `dir` (or cwd, if `dir` is invalid)*/
error_t file_lookup (file_t dir, char *name,
                     int flags0, /*try to open with these flags first */
                     int flags1, /*try to open with these flags, if
				   `flags0` fail */
                     int mode,	 /*if the file is to be created, create
				  it with this mode */
                     file_t * port, /*store the port to the looked up
				      file here */
                     io_statbuf_t * stat /*store the stat information here */
                    )
{
    error_t err = 0;

    /*The port to the looked up file */
    file_t p;

    /*The stat information about the looked up file */
    io_statbuf_t s;

    /*Performs a lookup under 'dir' or in cwd, if `dir` is invalid */
    file_t do_file_lookup (file_t dir, char *name,	/*lookup this file */
                           int flags,	/*lookup the file with these flags */
                           int mode	/*if a new file is to be
					  created, create it with this
					  mode */
                          )
    {
        /*The result of lookup */
        file_t p;

        /*If `dir` is a valid port */
        if (dir != MACH_PORT_NULL)
            /*try to lookup `name` under `dir` */
            p = file_name_lookup_under (dir, name, flags, mode);
        else
            /*lookup `name` under current cwd */
            p = file_name_lookup (name, flags, mode);

        /*Return the result of the lookup */
        return p;
    }				/*do_file_lookup */
Beispiel #14
0
static error_t
parser (int key, char *arg, struct argp_state *state)
{
  switch (key)
    {
    case 's': synchronous = 1; break;
    case 'c': do_children = 0; break;

    case ARGP_KEY_NO_ARGS:
      sync_one ("/", getcrdir ());
      break;

    case ARGP_KEY_ARG:
      sync_one (arg, file_name_lookup (arg, 0, 0));
      break;

    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}
Beispiel #15
0
/* Find an unoccupied (one with no active translator) filename starting with
   NAME_PFX, and start the translator for CTL_PORT on it.  If successful, this
   call will not return until the translator is stopped; otherwise it returns
   an error code describing the reason why it couldn't start.  When
   successful, this function sets UTMP_NODE_NAME to the name of the file we
   started the translator on.  */
static error_t
find_node_and_start_translator(char *name_pfx, fsys_t ctl_port)
{
  error_t err;
  int num = 0;

  do
    {
      file_t node;

      sprintf(utmp_node_name, "%s:%d", name_pfx, num++);

      node = file_name_lookup(utmp_node_name, O_CREAT | O_NOTRANS, 0666);
      if (node == MACH_PORT_NULL)
	err = errno;
      else
	err = start_translator(node, ctl_port);
    }
  while (err == EBUSY);

  return err;
}
Beispiel #16
0
/* Change current root directory to ARGV[0]. */
error_t
cmd_chcrdir (pid_t pid, mach_port_t msgport, int argc, char *argv[])
{
  error_t err;
  file_t dir;
  task_t task;
  process_t proc = getproc ();

  dir = file_name_lookup (argv[0], 0, 0);
  if (dir == MACH_PORT_NULL)
    return errno;
  err = proc_pid2task (proc, pid, &task);
  if (err)
    {
      mach_port_deallocate (mach_task_self (), dir);
      return err;
    }
  err = msg_set_init_port (msgport, task, INIT_PORT_CRDIR, dir,
			   MACH_MSG_TYPE_COPY_SEND);
  mach_port_deallocate (mach_task_self (), dir);
  mach_port_deallocate (mach_task_self (), task);
  return err;
}
Beispiel #17
0
int
main(int argc, char *argv[])
{
  error_t err;

  /* The filesystem node we're putting a translator on.  */
  char *node_name = 0;
  file_t node;

  /* The translator's arg vector, in '\0' separated format.  */
  char *argz = 0;
  size_t argz_len = 0;

  /* The control port for any active translator we start up.  */
  fsys_t active_control = MACH_PORT_NULL;

  /* Flags to pass to file_set_translator.  */
  int active_flags = 0;
  int passive_flags = 0;
  int lookup_flags = O_NOTRANS;
  int goaway_flags = 0;

  /* Various option flags.  */
  int passive = 0, active = 0, keep_active = 0, pause = 0, kill_active = 0,
      orphan = 0;
  int start = 0;
  int stack = 0;
  char *pid_file = NULL;
  int excl = 0;
  int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
  char *underlying_node_name = NULL;
  int underlying_lookup_flags;
  char **chroot_command = 0;
  char *chroot_chdir = "/";

  /* Parse our options...  */
  error_t parse_opt (int key, char *arg, struct argp_state *state)
    {
      switch (key)
	{
	case ARGP_KEY_ARG:
	  if (state->arg_num == 0)
	    node_name = arg;
	  else			/* command */
	    {
	      if (start)
		argp_error (state, "both --start and TRANSLATOR given");

	      error_t err =
		argz_create (state->argv + state->next - 1, &argz, &argz_len);
	      if (err)
		error(3, err, "Can't create options vector");
	      state->next = state->argc; /* stop parsing */
	    }
	  break;

	case ARGP_KEY_NO_ARGS:
	  argp_usage (state);
	  return EINVAL;

	case 'a': active = 1; break;
	case 's':
	  start = 1;
	  active = 1;	/* start implies active */
	  break;
	case OPT_STACK:
	  stack = 1;
	  active = 1;	/* stack implies active */
	  orphan = 1;	/* stack implies orphan */
	  break;
	case 'p': passive = 1; break;
	case 'k': keep_active = 1; break;
	case 'g': kill_active = 1; break;
	case 'x': excl = 1; break;
	case 'P': pause = 1; break;
	case 'F':
	  pid_file = strdup (arg);
	  if (pid_file == NULL)
	    error(3, ENOMEM, "Failed to duplicate argument");
	  break;

	case 'o': orphan = 1; break;
	case 'U':
	  underlying_node_name = strdup (arg);
	  if (underlying_node_name == NULL)
	    error(3, ENOMEM, "Failed to duplicate argument");
	  break;

	case 'C':
	  if (chroot_command)
	    {
	      argp_error (state, "--chroot given twice");
	      return EINVAL;
	    }
	  chroot_command = &state->argv[state->next];
	  while (state->next < state->argc)
	    {
	      if (!strcmp (state->argv[state->next], "--"))
		{
		  state->argv[state->next++] = 0;
		  if (chroot_command[0] == 0)
		    {
		      argp_error (state,
				  "--chroot must be followed by a command");
		      return EINVAL;
		    }
		  return 0;
		}
	      ++state->next;
	    }
	  argp_error (state, "--chroot command must be terminated with `--'");
	  return EINVAL;

	case OPT_CHROOT_CHDIR:
	  if (arg[0] != '/')
	    argp_error (state, "--chroot-chdir must be absolute");
	  chroot_chdir = arg;
	  break;

	case 'c': lookup_flags |= O_CREAT; break;
	case 'L': lookup_flags &= ~O_NOTRANS; break;

	case 'R': goaway_flags |= FSYS_GOAWAY_RECURSE; break;
	case 'S': goaway_flags |= FSYS_GOAWAY_NOSYNC; break;
	case 'f': goaway_flags |= FSYS_GOAWAY_FORCE; break;

	  /* Use atof so the user can specifiy fractional timeouts.  */
	case 't': timeout = atof (arg) * 1000.0; break;

	default:
	  return ARGP_ERR_UNKNOWN;
	}
      return 0;
    }
  struct argp argp = {options, parse_opt, args_doc, doc};

  argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);

  if (stack)
    {
      underlying_node_name = node_name;
      underlying_lookup_flags = lookup_flags && ~O_NOTRANS;
    }
  else
    underlying_lookup_flags = lookup_flags;

  if (!active && !passive && !chroot_command)
    passive = 1;		/* By default, set the passive translator.  */

  if (passive)
    passive_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0);
  if (active)
    active_flags = FS_TRANS_SET | (excl ? FS_TRANS_EXCL : 0)
		   | (orphan ? FS_TRANS_ORPHAN : 0);

  if (passive && !active)
    {
      /* When setting just the passive, decide what to do with any active.  */
      if (kill_active)
	/* Make it go away.  */
	active_flags = FS_TRANS_SET;
      else if (! keep_active)
	/* Ensure that there isn't one.  */
	active_flags = FS_TRANS_SET | FS_TRANS_EXCL;
    }

  if (start)
    {
      /* Retrieve the passive translator record in argz.  */
      mach_port_t node = file_name_lookup (node_name, lookup_flags, 0);
      if (node == MACH_PORT_NULL)
	error (4, errno, "%s", node_name);

      char buf[1024];
      argz = buf;
      argz_len = sizeof (buf);

      err = file_get_translator (node, &argz, &argz_len);
      if (err == EINVAL)
	error (4, 0, "%s: no passive translator record found", node_name);
      if (err)
	error (4, err, "%s", node_name);

      mach_port_deallocate (mach_task_self (), node);
    }

  if ((active || chroot_command) && argz_len > 0)
    {
      /* Error during file lookup; we use this to avoid duplicating error
	 messages.  */
      error_t open_err = 0;

      /* The callback to start_translator opens NODE as a side effect.  */
      error_t open_node (int flags,
			 mach_port_t *underlying,
			 mach_msg_type_name_t *underlying_type,
			 task_t task, void *cookie)
	{
	  if (pause)
	    {
	      fprintf (stderr, "Translator pid: %d\nPausing...",
	               task2pid (task));
	      getchar ();
	    }

	  if (pid_file != NULL)
	    {
	      FILE *h;
	      h = fopen (pid_file, "w");
	      if (h == NULL)
		error (4, errno, "Failed to open pid file");

	      fprintf (h, "%i\n", task2pid (task));
	      fclose (h);
	    }

	  node = file_name_lookup (node_name, flags | lookup_flags, 0666);
	  if (node == MACH_PORT_NULL)
	    {
	      open_err = errno;
	      return open_err;
	    }

	  if (underlying_node_name)
	    {
	      *underlying = file_name_lookup (underlying_node_name,
					      flags | underlying_lookup_flags,
					      0666);
	      if (! MACH_PORT_VALID (*underlying))
		{
		  /* For the error message.  */
		  node_name = underlying_node_name;
		  open_err = errno;
		  return open_err;
		}
	    }
	  else
	    *underlying = node;
	  *underlying_type = MACH_MSG_TYPE_COPY_SEND;

	  return 0;
	}
      err = fshelp_start_translator (open_node, NULL, argz, argz, argz_len,
				     timeout, &active_control);
      if (err)
	/* If ERR is due to a problem opening the translated node, we print
	   that name, otherwise, the name of the translator.  */
	error(4, err, "%s", (err == open_err) ? node_name : argz);
    }
Beispiel #18
0
/*Traces the translator stack on the given underlying node until it
  finds the first translator called `name` and returns the port
  pointing to the translator sitting under this one.*/
error_t
  trace_find
  (mach_port_t underlying, const char *name, int flags, mach_port_t * port)
{
  error_t err = 0;

  /*Identity information about the current process */
  uid_t *uids;
  size_t nuids;

  gid_t *gids;
  size_t ngids;

  /*The name and arguments of the translator being passed now */
  char *argz = NULL;
  size_t argz_len = 0;

  /*The current working directory */
  char *cwd = NULL;

  /*The port to the directory containing the file pointed to by `underlying` */
  file_t dir;

  /*The unauthenticated version of `dir` */
  file_t unauth_dir;

  /*The control port of the current translator */
  fsys_t fsys;

  /*The port to the translator we are currently looking at */
  mach_port_t node = underlying;

  /*The port to the previous translator */
  mach_port_t prev_node = MACH_PORT_NULL;

  /*The retry name and retry type returned by fsys_getroot */
  string_t retry_name;
  retry_type retry;

  /*Finalizes the execution of this function */
  void finalize (void)
  {
    /*If there is a working directory, free it */
    if (cwd)
      free (cwd);

    /*If the ports to the directory exist */
    if (dir)
      PORT_DEALLOC (dir);
  }				/*finalize */

  /*Obtain the current working directory */
  cwd = getcwd (NULL, 0);
  if (!cwd)
    {
      LOG_MSG ("trace_find: Could not obtain cwd.");
      return EINVAL;
    }
  LOG_MSG ("trace_find: cwd: '%s'", cwd);

  /*Open a port to this directory */
  dir = file_name_lookup (cwd, 0, 0);
  if (!dir)
    {
      finalize ();
      return ENOENT;
    }

  /*Try to get the number of effective UIDs */
  nuids = geteuids (0, 0);
  if (nuids < 0)
    {
      finalize ();
      return EINVAL;
    }

  /*Allocate some memory for the UIDs on the stack */
  uids = alloca (nuids * sizeof (uid_t));

  /*Fetch the UIDs themselves */
  nuids = geteuids (nuids, uids);
  if (nuids < 0)
    {
      finalize ();
      return EINVAL;
    }

  /*Try to get the number of effective GIDs */
  ngids = getgroups (0, 0);
  if (ngids < 0)
    {
      finalize ();
      return EINVAL;
    }

  /*Allocate some memory for the GIDs on the stack */
  gids = alloca (ngids * sizeof (gid_t));

  /*Fetch the GIDs themselves */
  ngids = getgroups (ngids, gids);
  if (ngids < 0)
    {
      finalize ();
      return EINVAL;
    }

  /*Obtain the unauthenticated version of `dir` */
  err = io_restrict_auth (dir, &unauth_dir, 0, 0, 0, 0);
  if (err)
    {
      finalize ();
      return err;
    }

  char buf[256];
  char *_buf = buf;
  size_t len = 256;
  io_read (node, &_buf, &len, 0, len);
  LOG_MSG ("trace_find: Read from underlying: '%s'", buf);

  /*Go up the translator stack */
  for (; !err;)
    {
      /*retreive the name and options of the current translator */
      err = file_get_fs_options (node, &argz, &argz_len);
      if (err)
	break;

      LOG_MSG ("trace_find: Obtained translator '%s'", argz);
      if (strcmp (argz, name) == 0)
	{
	  LOG_MSG ("trace_find: Match. Stopping here.");
	  break;
	}

      /*try to fetch the control port for this translator */
      err = file_get_translator_cntl (node, &fsys);
      LOG_MSG ("trace_find: err = %d", (int) err);
      if (err)
	break;

      LOG_MSG ("trace_find: Translator control port: %lu",
	       (unsigned long) fsys);

      prev_node = node;

      /*fetch the root of the translator */
      err = fsys_getroot
	(fsys, unauth_dir, MACH_MSG_TYPE_COPY_SEND,
	 uids, nuids, gids, ngids,
	 flags | O_NOTRANS, &retry, retry_name, &node);

      LOG_MSG ("trace_find: fsys_getroot returned %d", (int) err);
      LOG_MSG ("trace_find: Translator root: %lu", (unsigned long) node);

      /*TODO: Remove this debug output. */
      /*char buf[256];
         char * _buf = buf;
         size_t len = 256;
         io_read(node, &_buf, &len, 0, len);
         LOG_MSG("trace_find: Read: '%s'", buf); */
    }

  /*If the error occurred (most probably) because of the fact that we
     have reached the top of the translator stack */
  if ((err == EMACH_SEND_INVALID_DEST) || (err == ENXIO))
    /*this is OK */
    err = 0;

  /*Return the port to read from */
  *port = prev_node;

  /*Return the result of operations */
  finalize ();
  return err;
}				/*trace_find */
Beispiel #19
0
int
main (int argc, char **argv, char **envp)
{
  mach_port_t boot;
  error_t err;
  void *genport;
  process_t startup_port;
  mach_port_t startup;
  struct argp argp = { 0, 0, 0, "Hurd process server" };

  argp_parse (&argp, argc, argv, 0, 0, 0);

  initialize_version_info ();

  err = task_get_bootstrap_port (mach_task_self (), &boot);
  assert_perror (err);
  if (boot == MACH_PORT_NULL)
    error (2, 0, "proc server can only be run by init during boot");

  proc_bucket = ports_create_bucket ();
  proc_class = ports_create_class (0, 0);
  generic_port_class = ports_create_class (0, 0);
  exc_class = ports_create_class (exc_clean, 0);
  ports_create_port (generic_port_class, proc_bucket,
		     sizeof (struct port_info), &genport);
  generic_port = ports_get_right (genport);

  /* Create the initial proc object for init (PID 1).  */
  init_proc = create_init_proc ();

  /* Create the startup proc object for /hurd/init (PID 2).  */
  startup_proc = allocate_proc (MACH_PORT_NULL);
  startup_proc->p_deadmsg = 1;
  complete_proc (startup_proc, HURD_PID_STARTUP);

  /* Create our own proc object.  */
  self_proc = allocate_proc (mach_task_self ());
  assert (self_proc);

  complete_proc (self_proc, HURD_PID_PROC);

  startup_port = ports_get_send_right (startup_proc);
  err = startup_procinit (boot, startup_port, &startup_proc->p_task,
			  &authserver, &_hurd_host_priv, &_hurd_device_master);
  assert_perror (err);
  mach_port_deallocate (mach_task_self (), startup_port);

  mach_port_mod_refs (mach_task_self (), authserver, MACH_PORT_RIGHT_SEND, 1);
  _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
  mach_port_deallocate (mach_task_self (), boot);

  proc_death_notify (startup_proc);
  add_proc_to_hash (startup_proc); /* Now that we have the task port.  */

  /* Set our own argv and envp locations.  */
  self_proc->p_argv = (vm_address_t) argv;
  self_proc->p_envp = (vm_address_t) envp;

  /* Give ourselves good scheduling performance, because we are so
     important. */
  err = increase_priority ();
  if (err)
    error (0, err, "Increasing priority failed");

#if 0
  err = register_new_task_notification (_hurd_host_priv,
					generic_port,
					MACH_MSG_TYPE_MAKE_SEND);
  if (err)
    error (0, err, "Registering task notifications failed");
#endif

  {
    /* Get our stderr set up to print on the console, in case we have
       to panic or something.  */
    mach_port_t cons;
    error_t err;
    err = device_open (_hurd_device_master, D_READ|D_WRITE, "console", &cons);
    assert_perror (err);
    stdin = mach_open_devstream (cons, "r");
    stdout = stderr = mach_open_devstream (cons, "w");
    mach_port_deallocate (mach_task_self (), cons);
  }

  startup = file_name_lookup (_SERVERS_STARTUP, 0, 0);
  if (MACH_PORT_VALID (startup))
    {
      err = startup_essential_task (startup, mach_task_self (),
				    MACH_PORT_NULL, "proc", _hurd_host_priv);
      if (err)
	/* Due to the single-threaded nature of /hurd/startup, it can
	   only handle requests once the core server bootstrap has
	   completed.  Therefore, it does not bind itself to
	   /servers/startup until it is ready.	*/
	/* Fall back to abusing the message port lookup.  */
	startup_fallback = 1;

      err = mach_port_deallocate (mach_task_self (), startup);
      assert_perror (err);
    }
  else
    /* Fall back to abusing the message port lookup.	*/
    startup_fallback = 1;

  while (1)
    ports_manage_port_operations_multithread (proc_bucket,
					      message_demuxer,
					      0, 0, 0);
}
Beispiel #20
0
/* Mount one filesystem.  */
static error_t
do_mount (struct fs *fs, int remount)
{
    error_t err;
    char *fsopts, *o;
    size_t fsopts_len;
    char *mntopts;
    size_t mntopts_len;
    fsys_t mounted;

    inline void explain (const char *command)
    {
        if (verbose)
        {
            const char *o;
            printf ("%s %s", command, fs->mntent.mnt_dir);
            for (o = fsopts; o; o = argz_next (fsopts, fsopts_len, o))
                printf (" %s", o);
            putchar ('\n');
        }
    }

    err = fs_fsys (fs, &mounted);
    if (err)
    {
        error (0, err, "cannot determine if %s is already mounted",
               fs->mntent.mnt_fsname);
        return err;
    }


    /* Produce an argz of translator option arguments from the
       given FS's options and the command-line options.  */

#define ARGZ(call)							      \
  err = argz_##call;							      \
  if (err)								      \
    error (3, ENOMEM, "collecting mount options");			      \

    if (fs->mntent.mnt_opts)
    {
        /* Append the fstab options to any specified on the command line.  */
        ARGZ (create_sep (fs->mntent.mnt_opts, ',', &mntopts, &mntopts_len));

        /* Remove the `noauto' and `bind' options, since they're for us not the
           filesystem.  */
        for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
        {
            if (strcmp (o, MNTOPT_NOAUTO) == 0)
                argz_delete (&mntopts, &mntopts_len, o);
            if (strcmp (o, "bind") == 0)
            {
                fs->mntent.mnt_type = strdup ("firmlink");
                if (!fs->mntent.mnt_type)
                    error (3, ENOMEM, "failed to allocate memory");
                argz_delete (&mntopts, &mntopts_len, o);
            }
        }

        ARGZ (append (&mntopts, &mntopts_len, options, options_len));
    }
    else
    {
        mntopts = options;
        mntopts_len = options_len;
    }

    /* Convert the list of options into a list of switch arguments.  */
    fsopts = 0;
    fsopts_len = 0;
    for (o = mntopts; o; o = argz_next (mntopts, mntopts_len, o))
        if (*o == '-')		/* Allow letter opts `-o -r,-E', BSD style.  */
        {
            ARGZ (add (&fsopts, &fsopts_len, o));
        }
        else if ((strcmp (o, "defaults") != 0) && (strlen (o) != 0))
        {
            /* Prepend `--' to the option to make a long option switch,
               e.g. `--ro' or `--rsize=1024'.  */
            char arg[2 + strlen (o) + 1];
            arg[0] = arg[1] = '-';
            memcpy (&arg[2], o, sizeof arg - 2);
            ARGZ (add (&fsopts, &fsopts_len, arg));
        }

    if (mntopts != options)
        free (mntopts);
#undef ARGZ

    if (remount)
    {
        if (mounted == MACH_PORT_NULL)
        {
            error (0, 0, "%s not already mounted", fs->mntent.mnt_fsname);
            return EBUSY;
        }

        /* Send an RPC to request the new options, including --update.  */
        explain ("fsysopts");
        err = fsys_set_options (mounted, fsopts, fsopts_len, 0);
        if (err)
            error (0, err, "cannot remount %s", fs->mntent.mnt_fsname);
        return err;
    }
    else
    {
        /* Error during file lookup; we use this to avoid duplicating error
        messages.  */
        error_t open_err = 0;
        /* The control port for any active translator we start up.  */
        fsys_t active_control;
        file_t node;		/* Port to the underlying node.  */
        struct fstype *type;

        /* The callback to start_translator opens NODE as a side effect.  */
        error_t open_node (int flags,
                           mach_port_t *underlying,
                           mach_msg_type_name_t *underlying_type,
                           task_t task, void *cookie)
        {
            node = file_name_lookup (fs->mntent.mnt_dir,
                                     flags | O_NOTRANS, 0666);
            if (node == MACH_PORT_NULL)
            {
                open_err = errno;
                return open_err;
            }

            *underlying = node;
            *underlying_type = MACH_MSG_TYPE_COPY_SEND;

            return 0;
        }

        /* Do not fail if there is an active translator if --fake is
           given. This mimics Linux mount utility more closely which
           just looks into the mtab file. */
        if (mounted != MACH_PORT_NULL && !fake)
        {
            error (0, 0, "%s already mounted", fs->mntent.mnt_fsname);
            return EBUSY;
        }

        if (strcmp (fs->mntent.mnt_type, "auto") == 0)
        {
#if HAVE_BLKID
            char *type =
                blkid_get_tag_value (NULL, "TYPE", fs->mntent.mnt_fsname);
            if (! type)
            {
                error (0, 0, "failed to detect file system type");
                return EFTYPE;
            }
            else
            {
                if (strcmp (type, "vfat") == 0)
                    fs->mntent.mnt_type = strdup ("fat");
                else
                    fs->mntent.mnt_type = strdup (type);
                if (! fs->mntent.mnt_type)
                    error (3, ENOMEM, "failed to allocate memory");
            }
#else
            fs->mntent.mnt_type = strdup ("ext2");
            if (! fs->mntent.mnt_type)
                error (3, ENOMEM, "failed to allocate memory");
#endif
        }

        err = fs_type (fs, &type);
        if (err)
        {
            error (0, err, "%s: cannot determine filesystem type",
                   fs->mntent.mnt_fsname);
            return err;
        }
        if (type->program == 0)
        {
            error (0, 0, "%s: filesystem type `%s' unknown",
                   fs->mntent.mnt_fsname, type->name);
            return EFTYPE;
        }

        /* Stick the translator program name in front of the option switches.  */
        err = argz_insert (&fsopts, &fsopts_len, fsopts, type->program);
        /* Now stick the device name on the end as the last argument.  */
        if (!err)
            err = argz_add (&fsopts, &fsopts_len, fs->mntent.mnt_fsname);
        if (err)
            error (3, ENOMEM, "collecting mount options");

        /* Now we have a translator command line argz in FSOPTS.  */

        if (fake) {
            /* Fake the translator startup. */
            mach_port_t underlying;
            mach_msg_type_name_t underlying_type;
            err = open_node (O_READ, &underlying, &underlying_type, 0, NULL);
            if (err)
                error (1, errno, "cannot mount on %s", fs->mntent.mnt_dir);

            mach_port_deallocate (mach_task_self (), underlying);

            /* See if the translator is at least executable. */
            if (access(type->program, X_OK) == -1)
                error (1, errno, "can not execute %s", type->program);

            return 0;
        }

        explain ("settrans -a");
        err = fshelp_start_translator (open_node, NULL, fsopts,
                                       fsopts, fsopts_len, timeout,
                                       &active_control);
        /* If ERR is due to a problem opening the translated node, we print
        that name, otherwise, the name of the translator.  */
        if (open_err)
            error (0, open_err, "cannot mount on %s", fs->mntent.mnt_dir);
        else if (err)
            error (0, err, "cannot start translator %s", fsopts);
        else
        {
            err = file_set_translator (node, 0, FS_TRANS_SET|FS_TRANS_EXCL, 0,
                                       0, 0,
                                       active_control, MACH_MSG_TYPE_COPY_SEND);
            if (err == EBUSY)
                error (0, 0, "%s already mounted on", fs->mntent.mnt_dir);
            else if (err)
                error (0, err, "cannot set translator on %s", fs->mntent.mnt_dir);
            if (err)
                fsys_goaway (active_control, FSYS_GOAWAY_FORCE);
            mach_port_deallocate (mach_task_self (), active_control);
        }

        return err;
    }
Beispiel #21
0
kern_return_t
S_exec_init (struct trivfs_protid *protid,
	     auth_t auth, process_t proc)
{
  mach_port_t host_priv, device_master, startup;
  error_t err;

  if (! protid || ! protid->isroot)
    return EPERM;

  _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], proc); /* Consume.  */
  _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], auth); /* Consume.  */

  /* Do initial setup with the proc server.  */
  _hurd_proc_init (save_argv, NULL, 0);

  procserver = getproc ();

  /* Have the proc server notify us when the canonical ints and ports
     change.  This will generate an immediate callback giving us the
     initial boot-time canonical sets.  */
  {
    struct iouser *user;
    struct trivfs_protid *cred;
    mach_port_t right;

    err = iohelp_create_empty_iouser (&user);
    assert_perror (err);
    err = trivfs_open (fsys, user, 0, MACH_PORT_NULL, &cred);
    assert_perror (err);

    right = ports_get_send_right (cred);
    proc_execdata_notify (procserver, right, MACH_MSG_TYPE_COPY_SEND);
    mach_port_deallocate (mach_task_self (), right);
  }

  err = get_privileged_ports (&host_priv, &device_master);
  assert_perror (err);

  err = open_console (device_master);
  assert_perror (err);
  mach_port_deallocate (mach_task_self (), device_master);

  proc_register_version (procserver, host_priv, "exec", "", HURD_VERSION);

  startup = file_name_lookup (_SERVERS_STARTUP, 0, 0);
  if (startup == MACH_PORT_NULL)
    {
      error (0, errno, "%s", _SERVERS_STARTUP);

      /* Fall back to abusing the message port lookup.  */
      err = proc_getmsgport (procserver, HURD_PID_STARTUP, &startup);
      assert_perror (err);
    }
  mach_port_deallocate (mach_task_self (), procserver);

  /* Call startup_essential task last; init assumes we are ready to
     run once we call it. */
  err = startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL,
				"exec", host_priv);
  assert_perror (err);
  mach_port_deallocate (mach_task_self (), startup);

  mach_port_deallocate (mach_task_self (), host_priv);

  return 0;
}
Beispiel #22
0
/* Unmount one filesystem.  */
static error_t
do_umount (struct fs *fs)
{
  error_t err = 0;

  file_t node = file_name_lookup (fs->mntent.mnt_dir, O_NOTRANS, 0666);
  if (node == MACH_PORT_NULL)
    {
      error (0, errno, "%s", fs->mntent.mnt_dir);
      return errno;
    }

  if (verbose)
    printf ("settrans -ag%s%s %s\n",
	    goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
	    goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
	    fs->mntent.mnt_dir);

  if (! fake)
    {
      err = file_set_translator (node,
				 0, active_flags, goaway_flags,
				 NULL, 0,
				 MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
      if (! err)
	{
	  if (strcmp (fs->mntent.mnt_fsname, "") != 0 &&
	      strcmp (fs->mntent.mnt_fsname, "none") != 0)
	    {
	      if (verbose)
		printf ("settrans -ag%s%s %s\n",
			goaway_flags & FSYS_GOAWAY_NOSYNC? "S": "",
			goaway_flags & FSYS_GOAWAY_FORCE? "f": "",
			fs->mntent.mnt_fsname);

	      file_t source = file_name_lookup (fs->mntent.mnt_fsname,
						O_NOTRANS,
						0666);
	      if (source == MACH_PORT_NULL)
		{
		  error (0, errno, "%s", fs->mntent.mnt_fsname);
		  return errno;
		}

	      err = file_set_translator (source,
					 0, active_flags, goaway_flags,
					 NULL, 0,
					 MACH_PORT_NULL,
					 MACH_MSG_TYPE_COPY_SEND);
	      if (!(goaway_flags & FSYS_GOAWAY_FORCE))
		err = 0;
	      if (err)
		error (0, err, "%s", fs->mntent.mnt_fsname);

	      mach_port_deallocate (mach_task_self (), source);

	    }
	}
      else
	{
	  error (0, err, "%s", fs->mntent.mnt_dir);

	  /* Try remounting readonly instead if requested.  */
	  if (readonly)
	    {
	      if (verbose)
		printf ("fsysopts %s --readonly\n", fs->mntent.mnt_dir);

	      error_t e = fs_set_readonly (fs, TRUE);
	      if (e)
		error (0, e, "%s", fs->mntent.mnt_dir);
	    }
	}
    }

  /* Deallocate the reference so that unmounting nested translators
     works properly.  */
  mach_port_deallocate (mach_task_self (), node);
  return err;
}
Beispiel #23
0
int
grub_util_hurd_get_disk_info (const char *dev, grub_uint32_t *secsize, grub_disk_addr_t *offset,
			      grub_disk_addr_t *size, char **parent)
{
  file_t file;
  mach_port_t *ports;
  int *ints;
  loff_t *offsets;
  char *data;
  error_t err;
  mach_msg_type_number_t num_ports = 0, num_ints = 0, num_offsets = 0, data_len = 0;

  file = file_name_lookup (dev, 0, 0);
  if (file == MACH_PORT_NULL)
    return 0;

  err = file_get_storage_info (file,
			       &ports, &num_ports,
			       &ints, &num_ints,
			       &offsets, &num_offsets,
			       &data, &data_len);

  if (num_ints < 1)
    grub_util_error (_("Storage info for `%s' does not include type"), dev);
  if (ints[0] != STORAGE_DEVICE)
    grub_util_error (_("`%s' is not a local disk"), dev);

  if (num_offsets != 2)
    grub_util_error (_("Storage info for `%s' does indicate neither plain partition nor plain disk"), dev);
  if (parent)
    {
      *parent = NULL;
      if (num_ints >= 5)
	{
	  size_t len = ints[4];
	  if (len > data_len)
	    len = data_len;
	  *parent = xmalloc (len+1);
	  memcpy (*parent, data, len);
	  (*parent)[len] = '\0';
	}
    }
  if (offset)
    *offset = offsets[0];
  if (size)
    *size = offsets[1];
  if (secsize)
    *secsize = ints[2];
  if (ports && num_ports > 0)
    {
      mach_msg_type_number_t i;
      for (i = 0; i < num_ports; i++)
        {
	  mach_port_t port = ports[i];
	  if (port != MACH_PORT_NULL)
	    mach_port_deallocate (mach_task_self(), port);
        }
      munmap ((caddr_t) ports, num_ports * sizeof (*ports));
    }

  if (ints && num_ints > 0)
    munmap ((caddr_t) ints, num_ints * sizeof (*ints));
  if (offsets && num_offsets > 0)
    munmap ((caddr_t) offsets, num_offsets * sizeof (*offsets));
  if (data && data_len > 0)
    munmap (data, data_len);
  mach_port_deallocate (mach_task_self (), file);

  return 1;
}
Beispiel #24
0
/*Initializes the port to the underlying filesystem for the root node*/
error_t node_init_root (node_t * node	/*the root node */
  )
{
  error_t err = 0;

  /*Acquire a lock for operations on the underlying filesystem */
  mutex_lock (&ulfs_lock);

  /*Open the port to the directory specified in `dir` */
  node->nn->port = file_name_lookup (dir, O_READ | O_DIRECTORY, 0);

  /*If the directory could not be opened */
  if (node->nn->port == MACH_PORT_NULL)
    {
      /*set the error code accordingly */
      err = errno;
      LOG_MSG ("node_init_root: Could not open the port for %s.", dir);

      /*release the lock and stop */
      mutex_unlock (&ulfs_lock);
      return err;
    }

  LOG_MSG ("node_init_root: Port for %s opened successfully.", dir);
  LOG_MSG ("\tPort: 0x%lX", (unsigned long) node->nn->port);

  /*Stat the root node */
  err = io_stat (node->nn->port, &node->nn_stat);
  if (err)
    {
      /*deallocate the port */
      PORT_DEALLOC (node->nn->port);

      LOG_MSG ("node_init_root: Could not stat the root node.");

      /*unlock the mutex and exit */
      mutex_unlock (&ulfs_lock);
      return err;
    }

  /*Set the path to the corresponding lnode to `dir` */
  node->nn->lnode->path = strdup (dir);
  if (!node->nn->lnode->path)
    {
      /*deallocate the port */
      PORT_DEALLOC (node->nn->port);

      /*unlock the mutex */
      mutex_unlock (&ulfs_lock);

      LOG_MSG ("node_init_root: Could not strdup the directory.");
      return ENOMEM;
    }

  /*The current position in dir */
  char *p = dir + strlen (dir);

  /*Go through the path from end to beginning */
  for (; p >= dir; --p)
    {
      /*If the current character is a '/' */
      if (*p == '/')
	{
	  /*If p is not the first character */
	  if (p > dir)
	    {
	      /*if this slash is escaped, skip it */
	      if (*(p - 1) == '\\')
		continue;
	    }

	  /*advance the pointer to the first character after the slash */
	  ++p;

	  /*stop */
	  break;
	}
    }

  LOG_MSG ("node_init_root: The name of root node is %s.", p);

  /*Set the name of the lnode to the last element in the path to dir */
  node->nn->lnode->name = strdup (p);
  /*If the name of the node could not be duplicated */
  if (!node->nn->lnode->name)
    {
      /*free the name of the path to the node and deallocate teh port */
      free (node->nn->lnode->path);
      PORT_DEALLOC (node->nn->port);

      /*unlock the mutex */
      mutex_unlock (&ulfs_lock);

      LOG_MSG ("node_init_root: Could not strdup the name of the root node.");
      return ENOMEM;
    }

  /*Compute the length of the name of the root node */
  node->nn->lnode->name_len = strlen (p);

  /*Release the lock for operations on the undelying filesystem */
  mutex_unlock (&ulfs_lock);

  /*Return the result of operations */
  return err;
}				/*node_init_root */
Beispiel #25
0
kern_return_t
diskfs_S_file_exec (struct protid *cred,
                    task_t task,
                    int flags,
                    char *argv,
                    size_t argvlen,
                    char *envp,
                    size_t envplen,
                    mach_port_t *fds,
                    size_t fdslen,
                    mach_port_t *portarray,
                    size_t portarraylen,
                    int *intarray,
                    size_t intarraylen,
                    mach_port_t *deallocnames,
                    size_t deallocnameslen,
                    mach_port_t *destroynames,
                    size_t destroynameslen)
{
    struct node *np;
    uid_t uid;
    gid_t gid;
    mode_t mode;
    int suid, sgid;
    struct protid *newpi;
    struct peropen *newpo;
    error_t err = 0;
    mach_port_t execserver;
    int cached_exec;
    struct hurd_userlink ulink;
    mach_port_t right;

#define RETURN(code) do { err = (code); goto out; } while (0)

    if (!cred)
        return EOPNOTSUPP;

    /* Get a light reference to the cached exec server port.  */
    execserver = _hurd_port_get (&_diskfs_exec_portcell, &ulink);
    cached_exec = (execserver != MACH_PORT_NULL);
    if (execserver == MACH_PORT_NULL)
    {
        /* No cached port.  Look up the canonical naming point.  */
        execserver = file_name_lookup (_SERVERS_EXEC, 0, 0);
        if (execserver == MACH_PORT_NULL)
            return EOPNOTSUPP;	/* No exec server, no exec.  */
        else
        {
            /* Install the newly-gotten exec server port for other
               threads to use, then get a light reference for this call.  */
            _hurd_port_set (&_diskfs_exec_portcell, execserver);
            execserver = _hurd_port_get (&_diskfs_exec_portcell, &ulink);
        }
    }

    np = cred->po->np;

    mutex_lock (&np->lock);
    mode = np->dn_stat.st_mode;
    uid = np->dn_stat.st_uid;
    gid = np->dn_stat.st_gid;
    mutex_unlock (&np->lock);

    if (_diskfs_noexec)
        RETURN (EACCES);

    if ((cred->po->openstat & O_EXEC) == 0)
        RETURN (EBADF);

    if (!((mode & (S_IXUSR|S_IXGRP|S_IXOTH))
            || ((mode & S_IUSEUNK) && (mode & (S_IEXEC << S_IUNKSHIFT)))))
        RETURN (EACCES);

    if ((mode & S_IFMT) == S_IFDIR)
        RETURN (EACCES);

    suid = mode & S_ISUID;
    sgid = mode & S_ISGID;
    if (!_diskfs_nosuid && (suid || sgid))
    {
        int secure = 0;
        error_t get_file_ids (struct idvec *uids, struct idvec *gids)
        {
            error_t err = idvec_merge (uids, cred->user->uids);
            if (! err)
                err = idvec_merge (gids, cred->user->gids);
            return err;
        }
Beispiel #26
0
static int
copy_internal (const char *src_path, const char *dst_path,
	       int new_dst,
	       dev_t device,
	       struct dir_list *ancestors,
	       const struct cp_options *x,
	       int command_line_arg,
	       int *copy_into_self,
	       int *rename_succeeded)
{
  struct stat src_sb;
  struct stat dst_sb;
  mode_t src_mode;
  mode_t src_type;
  char *earlier_file = NULL;
  char *dst_backup = NULL;
  int backup_succeeded = 0;
  int delayed_fail;
  int copied_as_regular = 0;
  int ran_chown = 0;
  int preserve_metadata;

  if (x->move_mode && rename_succeeded)
    *rename_succeeded = 0;

  *copy_into_self = 0;
  if ((*(x->xstat)) (src_path, &src_sb))
    {
      error (0, errno, _("cannot stat %s"), quote (src_path));
      return 1;
    }

  src_type = src_sb.st_mode;

  src_mode = src_sb.st_mode;

  if (S_ISDIR (src_type) && !x->recursive)
    {
      error (0, 0, _("omitting directory %s"), quote (src_path));
      return 1;
    }

  /* Detect the case in which the same source file appears more than
     once on the command line and no backup option has been selected.
     If so, simply warn and don't copy it the second time.
     This check is enabled only if x->src_info is non-NULL.  */
  if (command_line_arg)
    {
      if ( ! S_ISDIR (src_sb.st_mode)
	   && x->backup_type == none
	   && seen_file (x->src_info, src_path, &src_sb))
	{
	  error (0, 0, _("warning: source file %s specified more than once"),
		 quote (src_path));
	  return 0;
	}

      record_file (x->src_info, src_path, &src_sb);
    }

  if (!new_dst)
    {
      if ((*(x->xstat)) (dst_path, &dst_sb))
	{
	  if (errno != ENOENT)
	    {
	      error (0, errno, _("cannot stat %s"), quote (dst_path));
	      return 1;
	    }
	  else
	    {
	      new_dst = 1;
	    }
	}
      else
	{
	  int return_now;
	  int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb,
				 x, &return_now);
	  if (return_now)
	    return 0;

	  if (! ok)
	    {
	      error (0, 0, _("%s and %s are the same file"),
		     quote_n (0, src_path), quote_n (1, dst_path));
	      return 1;
	    }

	  if (!S_ISDIR (dst_sb.st_mode))
	    {
	      if (S_ISDIR (src_type))
		{
		  error (0, 0,
		     _("cannot overwrite non-directory %s with directory %s"),
			 quote_n (0, dst_path), quote_n (1, src_path));
		  return 1;
		}

	      /* Don't let the user destroy their data, even if they try hard:
		 This mv command must fail (likewise for cp):
		   rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
		 Otherwise, the contents of b/f would be lost.
		 In the case of `cp', b/f would be lost if the user simulated
		 a move using cp and rm.
		 Note that it works fine if you use --backup=numbered.  */
	      if (command_line_arg
		  && x->backup_type != numbered
		  && seen_file (x->dest_info, dst_path, &dst_sb))
		{
		  error (0, 0,
			 _("will not overwrite just-created %s with %s"),
			 quote_n (0, dst_path), quote_n (1, src_path));
		  return 1;
		}
	    }

	  if (!S_ISDIR (src_type))
	    {
	      if (S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0,
		       _("cannot overwrite directory %s with non-directory"),
			 quote (dst_path));
		  return 1;
		}

	      if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0)
		{
		  /* We're using --update and the source file is older
		     than the destination file, so there is no need to
		     copy or move.  */
		  /* Pretend the rename succeeded, so the caller (mv)
		     doesn't end up removing the source file.  */
		  if (rename_succeeded)
		    *rename_succeeded = 1;
		  return 0;
		}
	    }

	  /* When there is an existing destination file, we may end up
	     returning early, and hence not copying/moving the file.
	     This may be due to an interactive `negative' reply to the
	     prompt about the existing file.  It may also be due to the
	     use of the --reply=no option.  */
	  if (!S_ISDIR (src_type))
	    {
	      /* cp and mv treat -i and -f differently.  */
	      if (x->move_mode)
		{
		  if ((x->interactive == I_ALWAYS_NO
		       && UNWRITABLE (dst_path, dst_sb.st_mode))
		      || ((x->interactive == I_ASK_USER
			   || (x->interactive == I_UNSPECIFIED
			       && x->stdin_tty
			       && UNWRITABLE (dst_path, dst_sb.st_mode)))
			  && (overwrite_prompt (dst_path, &dst_sb), 1)
			  && ! yesno ()))
		    {
		      /* Pretend the rename succeeded, so the caller (mv)
			 doesn't end up removing the source file.  */
		      if (rename_succeeded)
			*rename_succeeded = 1;
		      return 0;
		    }
		}
	      else
		{
		  if (x->interactive == I_ALWAYS_NO
		      || (x->interactive == I_ASK_USER
			  && (overwrite_prompt (dst_path, &dst_sb), 1)
			  && ! yesno ()))
		    {
		      return 0;
		    }
		}
	    }

	  if (x->move_mode)
	    {
	      /* In move_mode, DEST may not be an existing directory.  */
	      if (S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0, _("cannot overwrite directory %s"),
			 quote (dst_path));
		  return 1;
		}

	      /* Don't allow user to move a directory onto a non-directory.  */
	      if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0,
		       _("cannot move directory onto non-directory: %s -> %s"),
			 quote_n (0, src_path), quote_n (0, dst_path));
		  return 1;
		}
	    }

	  if (x->backup_type != none && !S_ISDIR (dst_sb.st_mode))
	    {
	      char *tmp_backup = find_backup_file_name (dst_path,
							x->backup_type);
	      if (tmp_backup == NULL)
		xalloc_die ();

	      /* Detect (and fail) when creating the backup file would
		 destroy the source file.  Before, running the commands
		 cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
		 would leave two zero-length files: a and a~.  */
	      /* FIXME: but simply change e.g., the final a~ to `./a~'
		 and the source will still be destroyed.  */
	      if (STREQ (tmp_backup, src_path))
		{
		  const char *fmt;
		  fmt = (x->move_mode
		 ? _("backing up %s would destroy source;  %s not moved")
		 : _("backing up %s would destroy source;  %s not copied"));
		  error (0, 0, fmt,
			 quote_n (0, dst_path),
			 quote_n (1, src_path));
		  free (tmp_backup);
		  return 1;
		}

	      dst_backup = (char *) alloca (strlen (tmp_backup) + 1);
	      strcpy (dst_backup, tmp_backup);
	      free (tmp_backup);
	      if (rename (dst_path, dst_backup))
		{
		  if (errno != ENOENT)
		    {
		      error (0, errno, _("cannot backup %s"), quote (dst_path));
		      return 1;
		    }
		  else
		    {
		      dst_backup = NULL;
		    }
		}
	      else
		{
		  backup_succeeded = 1;
		}
	      new_dst = 1;
	    }
	  else if (! S_ISDIR (dst_sb.st_mode)
		   && (x->unlink_dest_before_opening
		       || (x->xstat == lstat
			   && ! S_ISREG (src_sb.st_mode))))
	    {
	      if (unlink (dst_path) && errno != ENOENT)
		{
		  error (0, errno, _("cannot remove %s"), quote (dst_path));
		  return 1;
		}
	      new_dst = 1;
	    }
	}
    }

  /* If the source is a directory, we don't always create the destination
     directory.  So --verbose should not announce anything until we're
     sure we'll create a directory. */
  if (x->verbose && !S_ISDIR (src_type))
    {
      printf ("%s -> %s", quote_n (0, src_path), quote_n (1, dst_path));
      if (backup_succeeded)
	printf (_(" (backup: %s)"), quote (dst_backup));
      putchar ('\n');
    }

  /* Associate the destination path with the source device and inode
     so that if we encounter a matching dev/ino pair in the source tree
     we can arrange to create a hard link between the corresponding names
     in the destination tree.

     Sometimes, when preserving links, we have to record dev/ino even
     though st_nlink == 1:
     - when using -H and processing a command line argument;
	that command line argument could be a symlink pointing to another
	command line argument.  With `cp -H --preserve=link', we hard-link
	those two destination files.
     - likewise for -L except that it applies to all files, not just
	command line arguments.

     Also record directory dev/ino when using --recursive.  We'll use that
     info to detect this problem: cp -R dir dir.  FIXME-maybe: ideally,
     directory info would be recorded in a separate hash table, since
     such entries are useful only while a single command line hierarchy
     is being copied -- so that separate table could be cleared between
     command line args.  Using the same hash table to preserve hard
     links means that it may not be cleared.  */

  if ((x->preserve_links
       && (1 < src_sb.st_nlink
	   || (command_line_arg
	       && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
	   || x->dereference == DEREF_ALWAYS))
      || (x->recursive && S_ISDIR (src_type)))
    {
      earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
    }

  /* Did we copy this inode somewhere else (in this command line argument)
     and therefore this is a second hard link to the inode?  */

  if (earlier_file)
    {
      /* Avoid damaging the destination filesystem by refusing to preserve
	 hard-linked directories (which are found at least in Netapp snapshot
	 directories).  */
      if (S_ISDIR (src_type))
	{
	  /* If src_path and earlier_file refer to the same directory entry,
	     then warn about copying a directory into itself.  */
	  if (same_name (src_path, earlier_file))
	    {
	      error (0, 0, _("cannot copy a directory, %s, into itself, %s"),
		     quote_n (0, top_level_src_path),
		     quote_n (1, top_level_dst_path));
	      *copy_into_self = 1;
	    }
	  else
	    {
	      error (0, 0, _("will not create hard link %s to directory %s"),
		     quote_n (0, dst_path), quote_n (1, earlier_file));
	    }

	  goto un_backup;
	}

      {
	int link_failed;

	link_failed = link (earlier_file, dst_path);

	/* If the link failed because of an existing destination,
	   remove that file and then call link again.  */
	if (link_failed && errno == EEXIST)
	  {
	    if (unlink (dst_path))
	      {
		error (0, errno, _("cannot remove %s"), quote (dst_path));
		goto un_backup;
	      }
	    link_failed = link (earlier_file, dst_path);
	  }

	if (link_failed)
	  {
	    error (0, errno, _("cannot create hard link %s to %s"),
		   quote_n (0, dst_path), quote_n (1, earlier_file));
	    goto un_backup;
	  }

	return 0;
      }
    }

  if (x->move_mode)
    {
      if (rename (src_path, dst_path) == 0)
	{
	  if (x->verbose && S_ISDIR (src_type))
	    printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
	  if (rename_succeeded)
	    *rename_succeeded = 1;

	  if (command_line_arg)
	    {
	      /* Record destination dev/ino/filename, so that if we are asked
		 to overwrite that file again, we can detect it and fail.  */
	      /* It's fine to use the _source_ stat buffer (src_sb) to get the
	         _destination_ dev/ino, since the rename above can't have
		 changed those, and `mv' always uses lstat.
		 We could limit it further by operating
		 only on non-directories.  */
	      record_file (x->dest_info, dst_path, &src_sb);
	    }

	  return 0;
	}

      /* FIXME: someday, consider what to do when moving a directory into
	 itself but when source and destination are on different devices.  */

      /* This happens when attempting to rename a directory to a
	 subdirectory of itself.  */
      if (errno == EINVAL

	  /* When src_path is on an NFS file system, some types of
	     clients, e.g., SunOS4.1.4 and IRIX-5.3, set errno to EIO
	     instead.  Testing for this here risks misinterpreting a real
	     I/O error as an attempt to move a directory into itself, so
	     FIXME: consider not doing this.  */
	  || errno == EIO

	  /* And with SunOS-4.1.4 client and OpenBSD-2.3 server,
	     we get ENOTEMPTY.  */
	  || errno == ENOTEMPTY)
	{
	  /* FIXME: this is a little fragile in that it relies on rename(2)
	     failing with a specific errno value.  Expect problems on
	     non-POSIX systems.  */
	  error (0, 0, _("cannot move %s to a subdirectory of itself, %s"),
		 quote_n (0, top_level_src_path),
		 quote_n (1, top_level_dst_path));

	  /* Note that there is no need to call forget_created here,
	     (compare with the other calls in this file) since the
	     destination directory didn't exist before.  */

	  *copy_into_self = 1;
	  /* FIXME-cleanup: Don't return zero here; adjust mv.c accordingly.
	     The only caller that uses this code (mv.c) ends up setting its
	     exit status to nonzero when copy_into_self is nonzero.  */
	  return 0;
	}

      /* WARNING: there probably exist systems for which an inter-device
	 rename fails with a value of errno not handled here.
	 If/as those are reported, add them to the condition below.
	 If this happens to you, please do the following and send the output
	 to the bug-reporting address (e.g., in the output of cp --help):
	   touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"'
	 where your current directory is on one partion and /tmp is the other.
	 Also, please try to find the E* errno macro name corresponding to
	 the diagnostic and parenthesized integer, and include that in your
	 e-mail.  One way to do that is to run a command like this
	   find /usr/include/. -type f \
	     | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null
	 where you'd replace `18' with the integer in parentheses that
	 was output from the perl one-liner above.
	 If necessary, of course, change `/tmp' to some other directory.  */
      if (errno != EXDEV)
	{
	  /* There are many ways this can happen due to a race condition.
	     When something happens between the initial xstat and the
	     subsequent rename, we can get many different types of errors.
	     For example, if the destination is initially a non-directory
	     or non-existent, but it is created as a directory, the rename
	     fails.  If two `mv' commands try to rename the same file at
	     about the same time, one will succeed and the other will fail.
	     If the permissions on the directory containing the source or
	     destination file are made too restrictive, the rename will
	     fail.  Etc.  */
	  error (0, errno,
		 _("cannot move %s to %s"),
		 quote_n (0, src_path), quote_n (1, dst_path));
	  forget_created (src_sb.st_ino, src_sb.st_dev);
	  return 1;
	}

      /* The rename attempt has failed.  Remove any existing destination
	 file so that a cross-device `mv' acts as if it were really using
	 the rename syscall.  */
      if (unlink (dst_path) && errno != ENOENT)
	{
	  error (0, errno,
	     _("inter-device move failed: %s to %s; unable to remove target"),
		 quote_n (0, src_path), quote_n (1, dst_path));
	  forget_created (src_sb.st_ino, src_sb.st_dev);
	  return 1;
	}

      new_dst = 1;
    }

  delayed_fail = 0;

  /* In certain modes (cp's --symbolic-link), and for certain file types
     (symlinks and hard links) it doesn't make sense to preserve metadata,
     or it's possible to preserve only some of it.
     In such cases, set this variable to zero.  */
  preserve_metadata = 1;

  if (S_ISDIR (src_type))
    {
      struct dir_list *dir;

      /* If this directory has been copied before during the
         recursion, there is a symbolic link to an ancestor
         directory of the symbolic link.  It is impossible to
         continue to copy this, unless we've got an infinite disk.  */

      if (is_ancestor (&src_sb, ancestors))
	{
	  error (0, 0, _("cannot copy cyclic symbolic link %s"),
		 quote (src_path));
	  goto un_backup;
	}

      /* Insert the current directory in the list of parents.  */

      dir = (struct dir_list *) alloca (sizeof (struct dir_list));
      dir->parent = ancestors;
      dir->ino = src_sb.st_ino;
      dir->dev = src_sb.st_dev;

      if (new_dst || !S_ISDIR (dst_sb.st_mode))
	{
	  /* Create the new directory writable and searchable, so
             we can create new entries in it.  */

	  if (mkdir (dst_path, (src_mode & x->umask_kill) | S_IRWXU))
	    {
	      error (0, errno, _("cannot create directory %s"),
		     quote (dst_path));
	      goto un_backup;
	    }

	  /* Insert the created directory's inode and device
             numbers into the search structure, so that we can
             avoid copying it again.  */

	  if (remember_created (dst_path))
	    goto un_backup;

	  if (x->verbose)
	    printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
	}

      /* Are we crossing a file system boundary?  */
      if (x->one_file_system && device != 0 && device != src_sb.st_dev)
	return 0;

      /* Copy the contents of the directory.  */

      if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir, x,
		    copy_into_self))
	{
	  /* Don't just return here -- otherwise, the failure to read a
	     single file in a source directory would cause the containing
	     destination directory not to have owner/perms set properly.  */
	  delayed_fail = 1;
	}
    }
#ifdef S_ISLNK
  else if (x->symbolic_link)
    {
      preserve_metadata = 0;

      if (*src_path != '/')
	{
	  /* Check that DST_PATH denotes a file in the current directory.  */
	  struct stat dot_sb;
	  struct stat dst_parent_sb;
	  char *dst_parent;
	  int in_current_dir;

	  dst_parent = dir_name (dst_path);

	  in_current_dir = (STREQ (".", dst_parent)
			    /* If either stat call fails, it's ok not to report
			       the failure and say dst_path is in the current
			       directory.  Other things will fail later.  */
			    || stat (".", &dot_sb)
			    || stat (dst_parent, &dst_parent_sb)
			    || SAME_INODE (dot_sb, dst_parent_sb));
	  free (dst_parent);

	  if (! in_current_dir)
	    {
	      error (0, 0,
	   _("%s: can make relative symbolic links only in current directory"),
		     quote (dst_path));
	      goto un_backup;
	    }
	}
      if (symlink (src_path, dst_path))
	{
	  error (0, errno, _("cannot create symbolic link %s to %s"),
		 quote_n (0, dst_path), quote_n (1, src_path));
	  goto un_backup;
	}
    }
#endif
  else if (x->hard_link)
    {
      preserve_metadata = 0;
      if (link (src_path, dst_path))
	{
	  error (0, errno, _("cannot create link %s"), quote (dst_path));
	  goto un_backup;
	}
    }
  else if (S_ISREG (src_type)
	   || (x->copy_as_regular && !S_ISDIR (src_type)
#ifdef S_ISLNK
	       && !S_ISLNK (src_type)
#endif
	       ))
    {
      copied_as_regular = 1;
      /* POSIX says the permission bits of the source file must be
	 used as the 3rd argument in the open call, but that's not consistent
	 with historical practice.  */
      if (copy_reg (src_path, dst_path, x,
		    get_dest_mode (x, src_mode), &new_dst, &src_sb))
	goto un_backup;
    }
  else
#ifdef S_ISFIFO
  if (S_ISFIFO (src_type))
    {
      if (mkfifo (dst_path, get_dest_mode (x, src_mode)))
	{
	  error (0, errno, _("cannot create fifo %s"), quote (dst_path));
	  goto un_backup;
	}
    }
  else
#endif
    if (S_ISBLK (src_type) || S_ISCHR (src_type)
#ifdef S_ISSOCK
	|| S_ISSOCK (src_type)
#endif
	)
    {
      if (mknod (dst_path, get_dest_mode (x, src_mode), src_sb.st_rdev))
	{
	  error (0, errno, _("cannot create special file %s"),
		 quote (dst_path));
	  goto un_backup;
	}
    }
  else
#ifdef S_ISLNK
  if (S_ISLNK (src_type))
    {
      char *src_link_val = xreadlink (src_path);
      if (src_link_val == NULL)
	{
	  error (0, errno, _("cannot read symbolic link %s"), quote (src_path));
	  goto un_backup;
	}

      if (!symlink (src_link_val, dst_path))
	free (src_link_val);
      else
	{
	  int saved_errno = errno;
	  int same_link = 0;
	  if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode))
	    {
	      /* See if the destination is already the desired symlink.  */
	      size_t src_link_len = strlen (src_link_val);
	      char *dest_link_val = (char *) alloca (src_link_len + 1);
	      int dest_link_len = readlink (dst_path, dest_link_val,
					    src_link_len + 1);
	      if ((size_t) dest_link_len == src_link_len
		  && strncmp (dest_link_val, src_link_val, src_link_len) == 0)
		same_link = 1;
	    }
	  free (src_link_val);

	  if (! same_link)
	    {
	      error (0, saved_errno, _("cannot create symbolic link %s"),
		     quote (dst_path));
	      goto un_backup;
	    }
	}

      /* There's no need to preserve timestamps or permissions.  */
      preserve_metadata = 0;

      if (x->preserve_ownership)
	{
	  /* Preserve the owner and group of the just-`copied'
	     symbolic link, if possible.  */
# if HAVE_LCHOWN
	  if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid))
	    {
	      error (0, errno, _("failed to preserve ownership for %s"),
		     dst_path);
	      goto un_backup;
	    }
# else
	  /* Can't preserve ownership of symlinks.
	     FIXME: maybe give a warning or even error for symlinks
	     in directories with the sticky bit set -- there, not
	     preserving owner/group is a potential security problem.  */
# endif
	}
    }
  else
#endif
    {
      error (0, 0, _("%s has unknown file type"), quote (src_path));
      goto un_backup;
    }

  if (command_line_arg)
    record_file (x->dest_info, dst_path, NULL);

  if ( ! preserve_metadata)
    return 0;

  /* POSIX says that `cp -p' must restore the following:
     - permission bits
     - setuid, setgid bits
     - owner and group
     If it fails to restore any of those, we may give a warning but
     the destination must not be removed.
     FIXME: implement the above. */

  /* Adjust the times (and if possible, ownership) for the copy.
     chown turns off set[ug]id bits for non-root,
     so do the chmod last.  */

  if (x->preserve_timestamps)
    {
      struct utimbuf utb;

      /* There's currently no interface to set file timestamps with
	 better than 1-second resolution, so discard any fractional
	 part of the source timestamp.  */

      utb.actime = src_sb.st_atime;
      utb.modtime = src_sb.st_mtime;

      if (utime (dst_path, &utb))
	{
	  error (0, errno, _("preserving times for %s"), quote (dst_path));
	  if (x->require_preserve)
	    return 1;
	}
    }

  /* Avoid calling chown if we know it's not necessary.  */
  if (x->preserve_ownership
      && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb)))
    {
      ran_chown = 1;
      if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
	{
	  error (0, errno, _("failed to preserve ownership for %s"),
		 quote (dst_path));
	  if (x->require_preserve)
	    return 1;
	}
    }

#if HAVE_STRUCT_STAT_ST_AUTHOR
  /* Preserve the st_author field.  */
  {
    file_t file = file_name_lookup (dst_path, 0, 0);
    if (file_chauthor (file, src_sb.st_author))
      error (0, errno, _("failed to preserve authorship for %s"),
	     quote (dst_path));
    mach_port_deallocate (mach_task_self (), file);
  }
#endif

  /* Permissions of newly-created regular files were set upon `open' in
     copy_reg.  But don't return early if there were any special bits and
     we had to run chown, because the chown must have reset those bits.  */
  if ((new_dst && copied_as_regular)
      && !(ran_chown && (src_mode & ~S_IRWXUGO)))
    return delayed_fail;

  if ((x->preserve_mode || new_dst)
      && (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
    {
      if (chmod (dst_path, get_dest_mode (x, src_mode)))
	{
	  error (0, errno, _("setting permissions for %s"), quote (dst_path));
	  if (x->set_mode || x->require_preserve)
	    return 1;
	}
    }

  return delayed_fail;

un_backup:

  /* We have failed to create the destination file.
     If we've just added a dev/ino entry via the remember_copied
     call above (i.e., unless we've just failed to create a hard link),
     remove the entry associating the source dev/ino with the
     destination file name, so we don't try to `preserve' a link
     to a file we didn't create.  */
  if (earlier_file == NULL)
    forget_created (src_sb.st_ino, src_sb.st_dev);

  if (dst_backup)
    {
      if (rename (dst_backup, dst_path))
	error (0, errno, _("cannot un-backup %s"), quote (dst_path));
      else
	{
	  if (x->verbose)
	    printf (_("%s -> %s (unbackup)\n"),
		    quote_n (0, dst_backup), quote_n (1, dst_path));
	}
    }
  return 1;
}