36 |
char *netfs_server_version = HURD_VERSION; |
char *netfs_server_version = HURD_VERSION; |
37 |
int netfs_maxsymlinks = 16; /* arbitrary */ |
int netfs_maxsymlinks = 16; /* arbitrary */ |
38 |
|
|
39 |
|
static auth_t fakeroot_auth_port; |
40 |
|
|
41 |
struct netnode |
struct netnode |
42 |
{ |
{ |
43 |
void **idport_locp; /* easy removal pointer in idport ihash */ |
void **idport_locp; /* easy removal pointer in idport ihash */ |
58 |
struct ihash idport_ihash; |
struct ihash idport_ihash; |
59 |
|
|
60 |
|
|
61 |
|
/* Make a new virtual node. Always consumes the ports. */ |
62 |
static error_t |
static error_t |
63 |
new_node (file_t file, mach_port_t idport, int openmodes, struct node **np) |
new_node (file_t file, mach_port_t idport, int locked, int openmodes, |
64 |
|
struct node **np) |
65 |
{ |
{ |
66 |
error_t err; |
error_t err; |
67 |
struct netnode *nn = calloc (1, sizeof *nn); |
struct netnode *nn = calloc (1, sizeof *nn); |
70 |
mach_port_deallocate (mach_task_self (), file); |
mach_port_deallocate (mach_task_self (), file); |
71 |
if (idport != MACH_PORT_NULL) |
if (idport != MACH_PORT_NULL) |
72 |
mach_port_deallocate (mach_task_self (), idport); |
mach_port_deallocate (mach_task_self (), idport); |
73 |
|
if (locked) |
74 |
|
mutex_unlock (&idport_ihash_lock); |
75 |
return ENOMEM; |
return ENOMEM; |
76 |
} |
} |
77 |
nn->file = file; |
nn->file = file; |
82 |
{ |
{ |
83 |
int fileno; |
int fileno; |
84 |
mach_port_t fsidport; |
mach_port_t fsidport; |
85 |
|
assert (!locked); |
86 |
err = io_identity (file, &nn->idport, &fsidport, &fileno); |
err = io_identity (file, &nn->idport, &fsidport, &fileno); |
87 |
if (err) |
if (err) |
88 |
{ |
{ |
93 |
} |
} |
94 |
*np = netfs_make_node (nn); |
*np = netfs_make_node (nn); |
95 |
if (*np == 0) |
if (*np == 0) |
96 |
err = ENOMEM; |
{ |
97 |
|
if (locked) |
98 |
|
mutex_unlock (&idport_ihash_lock); |
99 |
|
err = ENOMEM; |
100 |
|
} |
101 |
else |
else |
102 |
{ |
{ |
103 |
mutex_lock (&idport_ihash_lock); |
if (!locked) |
104 |
|
mutex_lock (&idport_ihash_lock); |
105 |
err = ihash_add (&idport_ihash, nn->idport, *np, &nn->idport_locp); |
err = ihash_add (&idport_ihash, nn->idport, *np, &nn->idport_locp); |
106 |
|
if (!err) |
107 |
|
netfs_nref (*np); /* Return a reference to the caller. */ |
108 |
mutex_unlock (&idport_ihash_lock); |
mutex_unlock (&idport_ihash_lock); |
109 |
} |
} |
110 |
if (err) |
if (err) |
157 |
free (np); |
free (np); |
158 |
} |
} |
159 |
|
|
160 |
|
/* Given an existing node, make sure it has NEWMODES in its openmodes. |
161 |
|
If not null, FILE is a port with those openmodes. */ |
162 |
|
static error_t |
163 |
|
check_openmodes (struct netnode *nn, int newmodes, file_t file) |
164 |
|
{ |
165 |
|
error_t err = 0; |
166 |
|
|
167 |
|
if (newmodes &~ nn->openmodes) |
168 |
|
{ |
169 |
|
/* The user wants openmodes we haven't tried before. */ |
170 |
|
|
171 |
|
if (file != MACH_PORT_NULL && (nn->openmodes & ~newmodes)) |
172 |
|
{ |
173 |
|
/* Intersecting sets. |
174 |
|
We need yet another new peropen on this node. */ |
175 |
|
mach_port_deallocate (mach_task_self (), file); |
176 |
|
file = MACH_PORT_NULL; |
177 |
|
} |
178 |
|
if (file == MACH_PORT_NULL); |
179 |
|
{ |
180 |
|
enum retry_type bad_retry; |
181 |
|
char bad_retryname[1024]; /* XXX */ |
182 |
|
if (file != MACH_PORT_NULL) |
183 |
|
err = dir_lookup (nn->file, "", nn->openmodes | newmodes, 0, |
184 |
|
&bad_retry, bad_retryname, &file); |
185 |
|
if (!err && (bad_retry != FS_RETRY_NORMAL |
186 |
|
|| bad_retryname[0] != '\0')) |
187 |
|
{ |
188 |
|
mach_port_deallocate (mach_task_self (), file); |
189 |
|
err = EGRATUITOUS; |
190 |
|
} |
191 |
|
} |
192 |
|
if (! err) |
193 |
|
{ |
194 |
|
/* The new port has more openmodes than |
195 |
|
the old one. We can just use it now. */ |
196 |
|
mach_port_deallocate (mach_task_self (), nn->file); |
197 |
|
nn->file = file; |
198 |
|
nn->openmodes = newmodes; |
199 |
|
} |
200 |
|
} |
201 |
|
else if (file != MACH_PORT_NULL) |
202 |
|
mach_port_deallocate (mach_task_self (), file); |
203 |
|
|
204 |
|
return err; |
205 |
|
} |
206 |
|
|
207 |
|
/* This is called by netfs_S_fsys_getroot. */ |
208 |
|
error_t |
209 |
|
netfs_check_open_permissions (struct iouser *user, struct node *np, |
210 |
|
int flags, int newnode) |
211 |
|
{ |
212 |
|
return check_openmodes (np->nn, flags & (O_RDWR|O_EXEC), MACH_PORT_NULL); |
213 |
|
} |
214 |
|
|
215 |
|
error_t |
216 |
|
netfs_S_dir_lookup (struct protid *diruser, |
217 |
|
char *filename, |
218 |
|
int flags, |
219 |
|
mode_t mode, |
220 |
|
retry_type *do_retry, |
221 |
|
char *retry_name, |
222 |
|
mach_port_t *retry_port, |
223 |
|
mach_msg_type_name_t *retry_port_type) |
224 |
|
{ |
225 |
|
struct node *dnp, *np; |
226 |
|
error_t err; |
227 |
|
struct protid *newpi; |
228 |
|
struct iouser *user; |
229 |
|
mach_port_t file; |
230 |
|
mach_port_t idport, fsidport; |
231 |
|
int fileno; |
232 |
|
|
233 |
|
if (!diruser) |
234 |
|
return EOPNOTSUPP; |
235 |
|
|
236 |
|
dnp = diruser->po->np; |
237 |
|
err = dir_lookup (dnp->nn->file, filename, |
238 |
|
O_NOLINK | (flags |
239 |
|
& (O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK)), |
240 |
|
mode, do_retry, retry_name, &file); |
241 |
|
if (err) |
242 |
|
return err; |
243 |
|
|
244 |
|
switch (*do_retry) |
245 |
|
{ |
246 |
|
case FS_RETRY_REAUTH: |
247 |
|
{ |
248 |
|
mach_port_t ref = mach_reply_port (); |
249 |
|
err = io_reauthenticate (file, ref, MACH_MSG_TYPE_MAKE_SEND); |
250 |
|
if (! err) |
251 |
|
{ |
252 |
|
mach_port_deallocate (mach_task_self (), file); |
253 |
|
err = auth_user_authenticate (fakeroot_auth_port, ref, |
254 |
|
MACH_MSG_TYPE_MAKE_SEND, |
255 |
|
retry_port); |
256 |
|
} |
257 |
|
mach_port_destroy (mach_task_self (), ref); |
258 |
|
if (err) |
259 |
|
return err; |
260 |
|
} |
261 |
|
*do_retry = FS_RETRY_NORMAL; |
262 |
|
/*FALLTHROUGH*/ |
263 |
|
|
264 |
|
case FS_RETRY_NORMAL: |
265 |
|
case FS_RETRY_MAGICAL: |
266 |
|
default: |
267 |
|
if (file == MACH_PORT_NULL) |
268 |
|
{ |
269 |
|
*retry_port = MACH_PORT_NULL; |
270 |
|
*retry_port_type = MACH_MSG_TYPE_COPY_SEND; |
271 |
|
return 0; |
272 |
|
} |
273 |
|
break; |
274 |
|
} |
275 |
|
|
276 |
|
/* We have a new port to an underlying node. |
277 |
|
Find or make our corresponding virtual node. */ |
278 |
|
|
279 |
|
np = 0; |
280 |
|
err = io_identity (file, &idport, &fsidport, &fileno); |
281 |
|
if (err) |
282 |
|
mach_port_deallocate (mach_task_self (), file); |
283 |
|
else |
284 |
|
{ |
285 |
|
mach_port_deallocate (mach_task_self (), fsidport); |
286 |
|
if (fsidport == netfs_fsys_identity) |
287 |
|
{ |
288 |
|
/* Talking to ourselves! We just looked up one of our |
289 |
|
own nodes. Find the node and return it. */ |
290 |
|
struct protid *cred |
291 |
|
= ports_lookup_port (netfs_port_bucket, file, |
292 |
|
netfs_protid_class); |
293 |
|
mach_port_deallocate (mach_task_self (), idport); |
294 |
|
mach_port_deallocate (mach_task_self (), file); |
295 |
|
if (cred == 0) |
296 |
|
return EGRATUITOUS; |
297 |
|
np = cred->po->np; |
298 |
|
netfs_nref (np); |
299 |
|
ports_port_deref (cred); |
300 |
|
} |
301 |
|
else |
302 |
|
{ |
303 |
|
mutex_lock (&idport_ihash_lock); |
304 |
|
np = ihash_find (&idport_ihash, idport); |
305 |
|
if (np != 0) |
306 |
|
{ |
307 |
|
/* We already know about this node. */ |
308 |
|
mach_port_deallocate (mach_task_self (), idport); |
309 |
|
mutex_lock (&np->lock); |
310 |
|
err = check_openmodes (np->nn, (flags & (O_RDWR|O_EXEC)), file); |
311 |
|
if (!err) |
312 |
|
netfs_nref (np); |
313 |
|
mutex_unlock (&np->lock); |
314 |
|
mutex_unlock (&idport_ihash_lock); |
315 |
|
} |
316 |
|
else |
317 |
|
err = new_node (file, idport, 1, flags, &np); |
318 |
|
} |
319 |
|
} |
320 |
|
if (err) |
321 |
|
return err; |
322 |
|
|
323 |
|
if (retry_name[0] == '\0' && *do_retry == FS_RETRY_NORMAL) |
324 |
|
flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK); |
325 |
|
else |
326 |
|
flags = 0; |
327 |
|
|
328 |
|
err = iohelp_dup_iouser (&user, diruser->user); |
329 |
|
if (!err) |
330 |
|
{ |
331 |
|
newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), |
332 |
|
user); |
333 |
|
if (! newpi) |
334 |
|
{ |
335 |
|
iohelp_free_iouser (user); |
336 |
|
err = errno; |
337 |
|
} |
338 |
|
else |
339 |
|
{ |
340 |
|
*retry_port = ports_get_right (newpi); |
341 |
|
*retry_port_type = MACH_MSG_TYPE_MAKE_SEND; |
342 |
|
ports_port_deref (newpi); |
343 |
|
} |
344 |
|
} |
345 |
|
|
346 |
|
netfs_nrele (np); |
347 |
|
return err; |
348 |
|
} |
349 |
|
|
350 |
|
/* These callbacks are used only by the standard netfs_S_dir_lookup, |
351 |
|
which we do not use. But the shared library requires us to define them. */ |
352 |
|
error_t |
353 |
|
netfs_attempt_lookup (struct iouser *user, struct node *dir, |
354 |
|
char *name, struct node **np) |
355 |
|
{ |
356 |
|
assert (! "should not be here"); |
357 |
|
return EIEIO; |
358 |
|
} |
359 |
|
|
360 |
|
error_t |
361 |
|
netfs_attempt_create_file (struct iouser *user, struct node *dir, |
362 |
|
char *name, mode_t mode, struct node **np) |
363 |
|
{ |
364 |
|
assert (! "should not be here"); |
365 |
|
return EIEIO; |
366 |
|
} |
367 |
|
|
368 |
/* Make sure that NP->nn_stat is filled with the most current information. |
/* Make sure that NP->nn_stat is filled with the most current information. |
369 |
CRED identifies the user responsible for the operation. NP is locked. */ |
CRED identifies the user responsible for the operation. NP is locked. */ |
370 |
error_t |
error_t |
543 |
return 0; |
return 0; |
544 |
} |
} |
545 |
|
|
|
/* Lookup NAME in DIR (which is locked) for USER; set *NP to the found name |
|
|
upon return. If the name was not found, then return ENOENT. On any |
|
|
error, clear *NP. (*NP, if found, should be locked and a reference to |
|
|
it generated. This call should unlock DIR no matter what.) */ |
|
|
error_t |
|
|
netfs_attempt_lookup (struct iouser *user, struct node *dir, |
|
|
char *name, struct node **np) |
|
|
{ |
|
|
error_t err; |
|
|
int flags; |
|
|
const file_t dirfile = dir->nn->file; |
|
|
file_t file; |
|
|
|
|
|
/* We must unlock the directory before making RPCs to the underlying |
|
|
filesystem in case they somehow wind up trying to refer back to one of |
|
|
our nodes. The DIRFILE port will not change or die as long as DIR |
|
|
lives, and our caller holds a reference keeping it alive. */ |
|
|
mutex_unlock (&dir->lock); |
|
|
|
|
|
flags = O_RDWR|O_EXEC; |
|
|
file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); |
|
|
if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP |
|
|
|| errno == EROFS || errno == EISDIR)) |
|
|
{ |
|
|
flags = O_RDWR; |
|
|
file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); |
|
|
} |
|
|
if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP |
|
|
|| errno == EROFS || errno == EISDIR)) |
|
|
{ |
|
|
flags = O_READ|O_EXEC; |
|
|
file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); |
|
|
} |
|
|
if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP |
|
|
|| errno == EISDIR)) |
|
|
{ |
|
|
flags = O_READ; |
|
|
file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); |
|
|
} |
|
|
if (file == MACH_PORT_NULL && (errno == EACCES || errno == EOPNOTSUPP |
|
|
|| errno == EISDIR)) |
|
|
{ |
|
|
flags = 0; |
|
|
file = file_name_lookup_under (dirfile, name, flags | O_NOLINK, 0); |
|
|
} |
|
|
*np = 0; |
|
|
if (file == MACH_PORT_NULL) |
|
|
err = errno; |
|
|
else |
|
|
{ |
|
|
mach_port_t idport, fsidport; |
|
|
int fileno; |
|
|
err = io_identity (file, &idport, &fsidport, &fileno); |
|
|
if (err) |
|
|
mach_port_deallocate (mach_task_self (), file); |
|
|
else |
|
|
{ |
|
|
mach_port_deallocate (mach_task_self (), fsidport); |
|
|
if (fsidport == netfs_fsys_identity) |
|
|
{ |
|
|
/* Talking to ourselves! We just looked up one of our own nodes. |
|
|
Find the node and return it. */ |
|
|
struct protid *cred = ports_lookup_port (netfs_port_bucket, file, |
|
|
netfs_protid_class); |
|
|
mach_port_deallocate (mach_task_self (), idport); |
|
|
mach_port_deallocate (mach_task_self (), file); |
|
|
if (cred == 0) |
|
|
return EGRATUITOUS; |
|
|
*np = cred->po->np; |
|
|
netfs_nref (*np); |
|
|
ports_port_deref (cred); |
|
|
} |
|
|
else |
|
|
{ |
|
|
mutex_lock (&idport_ihash_lock); |
|
|
*np = ihash_find (&idport_ihash, idport); |
|
|
if (*np != 0) |
|
|
{ |
|
|
/* We already know about this node. */ |
|
|
mach_port_deallocate (mach_task_self (), idport); |
|
|
mach_port_deallocate (mach_task_self (), file); |
|
|
netfs_nref (*np); |
|
|
mutex_unlock (&idport_ihash_lock); |
|
|
} |
|
|
else |
|
|
{ |
|
|
mutex_unlock (&idport_ihash_lock); |
|
|
err = new_node (file, idport, flags, np); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (*np) |
|
|
mutex_lock (&(*np)->lock); |
|
|
return err; |
|
|
} |
|
|
|
|
546 |
error_t |
error_t |
547 |
netfs_attempt_mkdir (struct iouser *user, struct node *dir, |
netfs_attempt_mkdir (struct iouser *user, struct node *dir, |
548 |
char *name, mode_t mode) |
char *name, mode_t mode) |
592 |
file_t newfile; |
file_t newfile; |
593 |
error_t err = dir_mkfile (dir->nn->file, O_RDWR|O_EXEC, |
error_t err = dir_mkfile (dir->nn->file, O_RDWR|O_EXEC, |
594 |
real_from_fake_mode (mode), &newfile); |
real_from_fake_mode (mode), &newfile); |
|
if (err == 0) |
|
|
err = new_node (newfile, MACH_PORT_NULL, O_RDWR|O_EXEC, np); |
|
595 |
mutex_unlock (&dir->lock); |
mutex_unlock (&dir->lock); |
596 |
|
if (err == 0) |
597 |
|
err = new_node (newfile, MACH_PORT_NULL, 0, O_RDWR|O_EXEC, np); |
598 |
return err; |
return err; |
599 |
} |
} |
600 |
|
|
601 |
error_t |
error_t |
|
netfs_attempt_create_file (struct iouser *user, struct node *dir, |
|
|
char *name, mode_t mode, struct node **np) |
|
|
{ |
|
|
file_t newfile = file_name_lookup_under (dir->nn->file, name, |
|
|
O_CREAT|O_RDWR|O_EXEC, |
|
|
real_from_fake_mode (mode)); |
|
|
mutex_unlock (&dir->lock); |
|
|
if (newfile == MACH_PORT_NULL) |
|
|
return errno; |
|
|
return new_node (newfile, MACH_PORT_NULL, O_RDWR|O_EXEC, np); |
|
|
} |
|
|
|
|
|
error_t |
|
602 |
netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) |
netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf) |
603 |
{ |
{ |
604 |
char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1]; |
char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1]; |
623 |
} |
} |
624 |
|
|
625 |
error_t |
error_t |
|
netfs_check_open_permissions (struct iouser *user, struct node *np, |
|
|
int flags, int newnode) |
|
|
{ |
|
|
return (flags & (O_READ|O_WRITE|O_EXEC) & ~np->nn->openmodes) ? EACCES : 0; |
|
|
} |
|
|
|
|
|
error_t |
|
626 |
netfs_attempt_read (struct iouser *cred, struct node *np, |
netfs_attempt_read (struct iouser *cred, struct node *np, |
627 |
off_t offset, size_t *len, void *data) |
off_t offset, size_t *len, void *data) |
628 |
{ |
{ |
880 |
/* Parse our command line arguments (all none of them). */ |
/* Parse our command line arguments (all none of them). */ |
881 |
argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); |
argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); |
882 |
|
|
883 |
|
fakeroot_auth_port = getauth (); |
884 |
|
|
885 |
task_get_bootstrap_port (mach_task_self (), &bootstrap); |
task_get_bootstrap_port (mach_task_self (), &bootstrap); |
886 |
netfs_init (); |
netfs_init (); |
887 |
|
|
888 |
/* Get our underlying node (we presume it's a directory) and use |
/* Get our underlying node (we presume it's a directory) and use |
889 |
that to make the root node of the filesystem. */ |
that to make the root node of the filesystem. */ |
890 |
err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, O_READ, |
err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, 0, O_READ, |
891 |
&netfs_root_node); |
&netfs_root_node); |
892 |
if (err) |
if (err) |
893 |
error (5, err, "Cannot create root node"); |
error (5, err, "Cannot create root node"); |