summaryrefslogtreecommitdiff
path: root/sutils/clookup.c
blob: 9d31e958a716f9adce2697602928c37293c6cd71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* Careful filename lookup

   Copyright (C) 1996, 1998 Free Software Foundation, Inc.

   Written by Miles Bader <miles@gnu.ai.mit.edu>

   This file is part of the GNU Hurd.

   The GNU Hurd is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   The GNU Hurd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <hurd.h>
#include <hurd/lookup.h>
#include <hurd/id.h>
#include <hurd/fsys.h>

extern int __getuids (int num, gid_t *buf); /* XXX */

/* This function is like file_name_lookup, but tries hard to avoid starting
   any passive translators.  If a node with an unstarted passive translator
   is encountered, ENXIO is returned in ERRNO; other errors are as for
   file_name_lookup.  Note that checking for an active translator currently
   requires fetching the control port, which is a priveleged operation.  */
file_t
file_name_lookup_carefully (const char *name, int flags, mode_t mode)
{
  error_t err;
  file_t node;
  uid_t *uids;			/* Authentication of the current process.  */
  gid_t *gids;
  size_t num_uids, num_gids;

  /* Do the actual directory lookup.  We only do the first pathname element
     of NAME, appending the rest to any RETRY_NAME returned.  We then make
     sure the result node doesn't have a passive translator with no active
     translator started (but we make an exception for symlinks) -- if it
     does, we just return ENXIO.  */
  error_t lookup (file_t dir, char *name, int flags, mode_t mode,
		  retry_type *retry, string_t retry_name,
		  mach_port_t *node)
    {
      error_t err;
      char *head, *tail;
      char *slash = index (name, '/');

      if (slash)
	{
	  *stpncpy (head = alloca (slash - name + 1), name, slash - name) = 0;
	  tail = slash + 1;
	}
      else
	{
	  head = name;
	  tail = 0;
	}

      err = dir_lookup (dir, head, flags | O_NOTRANS, mode,
			retry, retry_name, node);
      if (err)
	return err;

      if (*node != MACH_PORT_NULL
	  && (!(flags & O_NOTRANS) || tail || *retry_name))
	/* The dir_lookup has returned a node to use for the next stage of
	   the lookup.  Unless it's the last element of the path and FLAGS
	   has O_NOTRANS set (in which case we just return what we got as
	   is), we have to simulate the above lookup being done without
	   O_NOTRANS.  Do this being careful not to start any translators.  */
	{
	  char _ptrans[1024], *ptrans = _ptrans;
	  size_t ptrans_len = sizeof _ptrans;

	  err = file_get_translator (*node, &ptrans, &ptrans_len);
	  if (! err)
	    /* Has a passive translator, see if there's an active one too.  */
	    {
	      fsys_t fsys;	/* Active translator control port.  */

	      if (ptrans != _ptrans)
		/* Deallocate out-of-line memory from file_get_translator.  */
		vm_deallocate (mach_task_self (),
			       (vm_address_t)ptrans, ptrans_len);

	      err = file_get_translator_cntl (*node, &fsys);
	      if (! err)
		/* There is!  Get its root node to use as the actual file.  */
		{
		  file_t unauth_dir; /* DIR unauthenticated.  */
		  err = io_restrict_auth (dir, &unauth_dir, 0, 0, 0, 0);
		  if (! err)
		    {
		      file_t old_node = *node;
		      err = fsys_getroot (fsys,
					  unauth_dir, MACH_MSG_TYPE_MOVE_SEND,
					  uids, num_uids, gids, num_gids,
					  flags & ~O_NOTRANS, retry,
					  retry_name, node);
		      if (! err)
			mach_port_deallocate (mach_task_self (), old_node);
		    }
		  mach_port_deallocate (mach_task_self (), fsys);
		}
	    }
	  else if (err == EINVAL)
	    /* No passive translator.  */
	    err = 0;

	  if (!err && tail)
	    /* Append TAIL to RETRY_NAME.  */
	    {
	      size_t rtn_len = strlen (retry_name);
	      if (rtn_len + 1 + strlen (tail) + 1 > sizeof (string_t))
		err = ENAMETOOLONG; /* Argh.  Lovely string_t. */
	      else
		{
		  if (rtn_len > 0 && retry_name[rtn_len - 1] != '/')
		    retry_name[rtn_len++] = '/';
		  strcpy (retry_name + rtn_len, tail);
		}
	    }

	  if (err)
	    mach_port_deallocate (mach_task_self (), *node);
	}

      return err;
    }

  /* Fetch uids for use with fsys_getroot.  */
  num_uids = __getuids (0, 0);
  if (num_uids < 0)
    return errno;
  uids = alloca (num_uids * sizeof (uid_t));
  num_uids = __getuids (num_uids, uids);
  if (num_uids < 0)
    return errno;

  /* ... and gids.  */
  num_gids = getgroups (0, 0);
  if (num_gids < 0)
    return errno;
  gids = alloca (num_gids * sizeof (gid_t));
  num_gids = getgroups (num_gids, gids);
  if (num_gids < 0)
    return errno;

  /* Look things up ...  */
  err = __hurd_file_name_lookup (&_hurd_ports_use, &getdport, lookup,
				 name, flags, mode & ~getumask (),
				 &node);

  return err ? (__hurd_fail (err), MACH_PORT_NULL) : node;
}