1 /* Linux namespaces(7) support.
3 Copyright (C) 2015-2020 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "gdbsupport/common-defs.h"
21 #include "nat/linux-namespaces.h"
22 #include "gdbsupport/filestuff.h"
24 #include <sys/syscall.h>
25 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include "gdbsupport/gdb_wait.h"
31 #include "gdbsupport/scope-exit.h"
33 /* See nat/linux-namespaces.h. */
34 bool debug_linux_namespaces
;
36 /* Handle systems without fork. */
49 /* Handle systems without setns. */
52 do_setns (int fd
, int nstype
)
55 return setns (fd
, nstype
);
56 #elif defined __NR_setns
57 return syscall (__NR_setns
, fd
, nstype
);
64 /* Handle systems without MSG_CMSG_CLOEXEC. */
66 #ifndef MSG_CMSG_CLOEXEC
67 #define MSG_CMSG_CLOEXEC 0
70 /* A Linux namespace. */
74 /* Filename of this namespace's entries in /proc/PID/ns. */
77 /* Nonzero if this object has been initialized. */
80 /* Nonzero if this namespace is supported on this system. */
83 /* ID of the namespace the calling process is in, used to
84 see if other processes share the namespace. The code in
85 this file assumes that the calling process never changes
90 /* Return the absolute filename of process PID's /proc/PID/ns
91 entry for namespace NS. The returned value persists until
92 this function is next called. */
95 linux_ns_filename (struct linux_ns
*ns
, int pid
)
97 static char filename
[PATH_MAX
];
100 xsnprintf (filename
, sizeof (filename
), "/proc/%d/ns/%s", pid
,
106 /* Return a representation of the caller's TYPE namespace, or
107 NULL if TYPE namespaces are not supported on this system. */
109 static struct linux_ns
*
110 linux_ns_get_namespace (enum linux_ns_type type
)
112 static struct linux_ns namespaces
[NUM_LINUX_NS_TYPES
] =
123 gdb_assert (type
>= 0 && type
< NUM_LINUX_NS_TYPES
);
124 ns
= &namespaces
[type
];
126 if (!ns
->initialized
)
130 if (stat (linux_ns_filename (ns
, getpid ()), &sb
) == 0)
140 return ns
->supported
? ns
: NULL
;
143 /* See nat/linux-namespaces.h. */
146 linux_ns_same (pid_t pid
, enum linux_ns_type type
)
148 struct linux_ns
*ns
= linux_ns_get_namespace (type
);
149 const char *filename
;
152 /* If the kernel does not support TYPE namespaces then there's
153 effectively only one TYPE namespace that all processes on
158 /* Stat PID's TYPE namespace entry to get the namespace ID. This
159 might fail if the process died, or if we don't have the right
160 permissions (though we should be attached by this time so this
161 seems unlikely). In any event, we can't make any decisions and
163 filename
= linux_ns_filename (ns
, pid
);
164 if (stat (filename
, &sb
) != 0)
165 perror_with_name (filename
);
167 return sb
.st_ino
== ns
->id
;
170 /* We need to use setns(2) to handle filesystem access in mount
171 namespaces other than our own, but this isn't permitted for
172 multithreaded processes. GDB is multithreaded when compiled
173 with Guile support, and may become multithreaded if compiled
174 with Python support. We deal with this by spawning a single-
175 threaded helper process to access mount namespaces other than
178 The helper process is started the first time a call to setns
179 is required. The main process (GDB or gdbserver) communicates
180 with the helper via sockets, passing file descriptors where
181 necessary using SCM_RIGHTS. Once started the helper process
182 runs until the main process terminates; when this happens the
183 helper will receive socket errors, notice that its parent died,
184 and exit accordingly (see mnsh_maybe_mourn_peer).
186 The protocol is that the main process sends a request in a
187 single message, and the helper replies to every message it
188 receives with a single-message response. If the helper
189 receives a message it does not understand it will reply with
190 a MNSH_MSG_ERROR message. The main process checks all
191 responses it receives with gdb_assert, so if the main process
192 receives something unexpected (which includes MNSH_MSG_ERROR)
193 the main process will call internal_error.
195 For avoidance of doubt, if the helper process receives a
196 message it doesn't handle it will reply with MNSH_MSG_ERROR.
197 If the main process receives MNSH_MSG_ERROR at any time then
198 it will call internal_error. If internal_error causes the
199 main process to exit, the helper will notice this and also
200 exit. The helper will not exit until the main process
201 terminates, so if the user continues through internal_error
202 the helper will still be there awaiting requests from the
205 Messages in both directions have the following payload:
207 - TYPE (enum mnsh_msg_type, always sent) - the message type.
209 - INT2 (int, always sent, though not always used) - two
210 values whose meaning is message-type-dependent.
211 See enum mnsh_msg_type documentation below.
212 - FD (int, optional, sent using SCM_RIGHTS) - an open file
214 - BUF (unstructured data, optional) - some data with message-
215 type-dependent meaning.
217 Note that the helper process is the child of a call to fork,
218 so all code in the helper must be async-signal-safe. */
220 /* Mount namespace helper message types. */
224 /* A communication error occurred. Receipt of this message
225 by either end will cause an assertion failure in the main
229 /* Requests, sent from the main process to the helper. */
231 /* A request that the helper call setns. Arguments should
232 be passed in FD and INT1. Helper should respond with a
236 /* A request that the helper call open. Arguments should
237 be passed in BUF, INT1 and INT2. The filename (in BUF)
238 should include a terminating NUL character. The helper
239 should respond with a MNSH_RET_FD. */
242 /* A request that the helper call unlink. The single
243 argument (the filename) should be passed in BUF, and
244 should include a terminating NUL character. The helper
245 should respond with a MNSH_RET_INT. */
248 /* A request that the helper call readlink. The single
249 argument (the filename) should be passed in BUF, and
250 should include a terminating NUL character. The helper
251 should respond with a MNSH_RET_INTSTR. */
254 /* Responses, sent to the main process from the helper. */
256 /* Return an integer in INT1 and errno in INT2. */
259 /* Return a file descriptor in FD if one was opened or an
260 integer in INT1 otherwise. Return errno in INT2. */
263 /* Return an integer in INT1, errno in INT2, and optionally
268 /* Print a string representation of a message using debug_printf.
269 This function is not async-signal-safe so should never be
270 called from the helper. */
273 mnsh_debug_print_message (enum mnsh_msg_type type
,
274 int fd
, int int1
, int int2
,
275 const void *buf
, int bufsiz
)
277 gdb_byte
*c
= (gdb_byte
*) buf
;
278 gdb_byte
*cl
= c
+ bufsiz
;
283 debug_printf ("ERROR");
287 debug_printf ("SETNS");
291 debug_printf ("OPEN");
294 case MNSH_REQ_UNLINK
:
295 debug_printf ("UNLINK");
298 case MNSH_REQ_READLINK
:
299 debug_printf ("READLINK");
303 debug_printf ("INT");
310 case MNSH_RET_INTSTR
:
311 debug_printf ("INTSTR");
315 debug_printf ("unknown-packet-%d", type
);
318 debug_printf (" %d %d %d \"", fd
, int1
, int2
);
321 debug_printf (*c
>= ' ' && *c
<= '~' ? "%c" : "\\%o", *c
);
326 /* Forward declaration. */
328 static void mnsh_maybe_mourn_peer (void);
330 /* Send a message. The argument SOCK is the file descriptor of the
331 sending socket, the other arguments are the payload to send.
332 Return the number of bytes sent on success. Return -1 on failure
333 and set errno appropriately. This function is called by both the
334 main process and the helper so must be async-signal-safe. */
337 mnsh_send_message (int sock
, enum mnsh_msg_type type
,
338 int fd
, int int1
, int int2
,
339 const void *buf
, int bufsiz
)
343 char fdbuf
[CMSG_SPACE (sizeof (fd
))];
346 /* Build the basic TYPE, INT1, INT2 message. */
347 memset (&msg
, 0, sizeof (msg
));
350 iov
[0].iov_base
= &type
;
351 iov
[0].iov_len
= sizeof (type
);
352 iov
[1].iov_base
= &int1
;
353 iov
[1].iov_len
= sizeof (int1
);
354 iov
[2].iov_base
= &int2
;
355 iov
[2].iov_len
= sizeof (int2
);
359 /* Append BUF if supplied. */
360 if (buf
!= NULL
&& bufsiz
> 0)
362 iov
[3].iov_base
= alloca (bufsiz
);
363 memcpy (iov
[3].iov_base
, buf
, bufsiz
);
364 iov
[3].iov_len
= bufsiz
;
369 /* Attach FD if supplied. */
372 struct cmsghdr
*cmsg
;
374 msg
.msg_control
= fdbuf
;
375 msg
.msg_controllen
= sizeof (fdbuf
);
377 cmsg
= CMSG_FIRSTHDR (&msg
);
378 cmsg
->cmsg_level
= SOL_SOCKET
;
379 cmsg
->cmsg_type
= SCM_RIGHTS
;
380 cmsg
->cmsg_len
= CMSG_LEN (sizeof (int));
382 memcpy (CMSG_DATA (cmsg
), &fd
, sizeof (int));
384 msg
.msg_controllen
= cmsg
->cmsg_len
;
387 /* Send the message. */
388 size
= sendmsg (sock
, &msg
, 0);
391 mnsh_maybe_mourn_peer ();
393 if (debug_linux_namespaces
)
395 debug_printf ("mnsh: send: ");
396 mnsh_debug_print_message (type
, fd
, int1
, int2
, buf
, bufsiz
);
397 debug_printf (" -> %s\n", pulongest (size
));
403 /* Receive a message. The argument SOCK is the file descriptor of
404 the receiving socket, the other arguments point to storage for
405 the received payload. Returns the number of bytes stored into
406 BUF on success, which may be zero in the event no BUF was sent.
407 Return -1 on failure and set errno appropriately. This function
408 is called from both the main process and the helper and must be
409 async-signal-safe. */
412 mnsh_recv_message (int sock
, enum mnsh_msg_type
*type
,
413 int *fd
, int *int1
, int *int2
,
414 void *buf
, int bufsiz
)
418 char fdbuf
[CMSG_SPACE (sizeof (*fd
))];
419 struct cmsghdr
*cmsg
;
420 ssize_t size
, fixed_size
;
423 /* Build the message to receive data into. */
424 memset (&msg
, 0, sizeof (msg
));
427 iov
[0].iov_base
= type
;
428 iov
[0].iov_len
= sizeof (*type
);
429 iov
[1].iov_base
= int1
;
430 iov
[1].iov_len
= sizeof (*int1
);
431 iov
[2].iov_base
= int2
;
432 iov
[2].iov_len
= sizeof (*int2
);
433 iov
[3].iov_base
= buf
;
434 iov
[3].iov_len
= bufsiz
;
438 for (fixed_size
= i
= 0; i
< msg
.msg_iovlen
- 1; i
++)
439 fixed_size
+= iov
[i
].iov_len
;
441 msg
.msg_control
= fdbuf
;
442 msg
.msg_controllen
= sizeof (fdbuf
);
444 /* Receive the message. */
445 size
= recvmsg (sock
, &msg
, MSG_CMSG_CLOEXEC
);
448 if (debug_linux_namespaces
)
449 debug_printf ("namespace-helper: recv failed (%s)\n",
452 mnsh_maybe_mourn_peer ();
457 /* Check for truncation. */
458 if (size
< fixed_size
|| (msg
.msg_flags
& (MSG_TRUNC
| MSG_CTRUNC
)))
460 if (debug_linux_namespaces
)
461 debug_printf ("namespace-helper: recv truncated (%s 0x%x)\n",
462 pulongest (size
), msg
.msg_flags
);
464 mnsh_maybe_mourn_peer ();
470 /* Unpack the file descriptor if supplied. */
471 cmsg
= CMSG_FIRSTHDR (&msg
);
473 && cmsg
->cmsg_len
== CMSG_LEN (sizeof (int))
474 && cmsg
->cmsg_level
== SOL_SOCKET
475 && cmsg
->cmsg_type
== SCM_RIGHTS
)
476 memcpy (fd
, CMSG_DATA (cmsg
), sizeof (int));
480 if (debug_linux_namespaces
)
482 debug_printf ("mnsh: recv: ");
483 mnsh_debug_print_message (*type
, *fd
, *int1
, *int2
, buf
,
488 /* Return the number of bytes of data in BUF. */
489 return size
- fixed_size
;
492 /* Shortcuts for returning results from the helper. */
494 #define mnsh_return_int(sock, result, error) \
495 mnsh_send_message (sock, MNSH_RET_INT, -1, result, error, NULL, 0)
497 #define mnsh_return_fd(sock, fd, error) \
498 mnsh_send_message (sock, MNSH_RET_FD, \
499 (fd) < 0 ? -1 : (fd), \
500 (fd) < 0 ? (fd) : 0, \
503 #define mnsh_return_intstr(sock, result, buf, bufsiz, error) \
504 mnsh_send_message (sock, MNSH_RET_INTSTR, -1, result, error, \
507 /* Handle a MNSH_REQ_SETNS message. Must be async-signal-safe. */
510 mnsh_handle_setns (int sock
, int fd
, int nstype
)
512 int result
= do_setns (fd
, nstype
);
514 return mnsh_return_int (sock
, result
, errno
);
517 /* Handle a MNSH_REQ_OPEN message. Must be async-signal-safe. */
520 mnsh_handle_open (int sock
, const char *filename
,
521 int flags
, mode_t mode
)
523 int fd
= gdb_open_cloexec (filename
, flags
, mode
);
524 ssize_t result
= mnsh_return_fd (sock
, fd
, errno
);
532 /* Handle a MNSH_REQ_UNLINK message. Must be async-signal-safe. */
535 mnsh_handle_unlink (int sock
, const char *filename
)
537 int result
= unlink (filename
);
539 return mnsh_return_int (sock
, result
, errno
);
542 /* Handle a MNSH_REQ_READLINK message. Must be async-signal-safe. */
545 mnsh_handle_readlink (int sock
, const char *filename
)
548 int len
= readlink (filename
, buf
, sizeof (buf
));
550 return mnsh_return_intstr (sock
, len
,
551 buf
, len
< 0 ? 0 : len
,
555 /* The helper process. Never returns. Must be async-signal-safe. */
557 static void mnsh_main (int sock
) ATTRIBUTE_NORETURN
;
564 enum mnsh_msg_type type
;
565 int fd
= -1, int1
, int2
;
567 ssize_t size
, response
= -1;
569 size
= mnsh_recv_message (sock
, &type
,
573 if (size
>= 0 && size
< sizeof (buf
))
579 response
= mnsh_handle_setns (sock
, fd
, int1
);
583 if (size
> 0 && buf
[size
- 1] == '\0')
584 response
= mnsh_handle_open (sock
, buf
, int1
, int2
);
587 case MNSH_REQ_UNLINK
:
588 if (size
> 0 && buf
[size
- 1] == '\0')
589 response
= mnsh_handle_unlink (sock
, buf
);
592 case MNSH_REQ_READLINK
:
593 if (size
> 0 && buf
[size
- 1] == '\0')
594 response
= mnsh_handle_readlink (sock
, buf
);
598 break; /* Handled below. */
602 /* Close any file descriptors we were passed. */
606 /* Can't handle this message, bounce it back. */
612 mnsh_send_message (sock
, MNSH_MSG_ERROR
,
613 -1, int1
, int2
, buf
, size
);
618 /* The mount namespace helper process. */
625 /* Socket for communication. */
628 /* ID of the mount namespace the helper is currently in. */
632 /* In the helper process this is set to the PID of the process that
633 created the helper (i.e. GDB or gdbserver). In the main process
634 this is set to zero. Used by mnsh_maybe_mourn_peer. */
635 static int mnsh_creator_pid
= 0;
637 /* Return an object representing the mount namespace helper process.
638 If no mount namespace helper process has been started then start
639 one. Return NULL if no mount namespace helper process could be
642 static struct linux_mnsh
*
643 linux_mntns_get_helper (void)
645 static struct linux_mnsh
*helper
= NULL
;
649 static struct linux_mnsh h
;
651 pid_t helper_creator
= getpid ();
654 ns
= linux_ns_get_namespace (LINUX_NS_MNT
);
658 if (gdb_socketpair_cloexec (AF_UNIX
, SOCK_STREAM
, 0, sv
) < 0)
664 int saved_errno
= errno
;
678 mnsh_creator_pid
= helper_creator
;
680 /* Debug printing isn't async-signal-safe. */
681 debug_linux_namespaces
= 0;
686 /* Parent process. */
690 helper
->sock
= sv
[0];
691 helper
->nsid
= ns
->id
;
693 if (debug_linux_namespaces
)
694 debug_printf ("Started mount namespace helper process %d\n",
701 /* Check whether the other process died and act accordingly. Called
702 whenever a socket error occurs, from both the main process and the
703 helper. Must be async-signal-safe when called from the helper. */
706 mnsh_maybe_mourn_peer (void)
708 if (mnsh_creator_pid
!= 0)
710 /* We're in the helper. Check if our current parent is the
711 process that started us. If it isn't, then our original
712 parent died and we've been reparented. Exit immediately
713 if that's the case. */
714 if (getppid () != mnsh_creator_pid
)
719 /* We're in the main process. */
721 struct linux_mnsh
*helper
= linux_mntns_get_helper ();
727 /* We already mourned it. */
731 pid
= waitpid (helper
->pid
, &status
, WNOHANG
);
734 /* The helper is still alive. */
740 warning (_("mount namespace helper vanished?"));
742 internal_warning (__FILE__
, __LINE__
,
743 _("unhandled error %d"), errno
);
745 else if (pid
== helper
->pid
)
747 if (WIFEXITED (status
))
748 warning (_("mount namespace helper exited with status %d"),
749 WEXITSTATUS (status
));
750 else if (WIFSIGNALED (status
))
751 warning (_("mount namespace helper killed by signal %d"),
754 internal_warning (__FILE__
, __LINE__
,
755 _("unhandled status %d"), status
);
758 internal_warning (__FILE__
, __LINE__
,
759 _("unknown pid %d"), pid
);
761 /* Something unrecoverable happened. */
766 /* Shortcuts for sending messages to the helper. */
768 #define mnsh_send_setns(helper, fd, nstype) \
769 mnsh_send_message (helper->sock, MNSH_REQ_SETNS, fd, nstype, 0, \
772 #define mnsh_send_open(helper, filename, flags, mode) \
773 mnsh_send_message (helper->sock, MNSH_REQ_OPEN, -1, flags, mode, \
774 filename, strlen (filename) + 1)
776 #define mnsh_send_unlink(helper, filename) \
777 mnsh_send_message (helper->sock, MNSH_REQ_UNLINK, -1, 0, 0, \
778 filename, strlen (filename) + 1)
780 #define mnsh_send_readlink(helper, filename) \
781 mnsh_send_message (helper->sock, MNSH_REQ_READLINK, -1, 0, 0, \
782 filename, strlen (filename) + 1)
784 /* Receive a message from the helper. Issue an assertion failure if
785 the message isn't a correctly-formatted MNSH_RET_INT. Set RESULT
786 and ERROR and return 0 on success. Set errno and return -1 on
790 mnsh_recv_int (struct linux_mnsh
*helper
, int *result
, int *error
)
792 enum mnsh_msg_type type
;
797 size
= mnsh_recv_message (helper
->sock
, &type
, &fd
,
803 gdb_assert (type
== MNSH_RET_INT
);
804 gdb_assert (fd
== -1);
805 gdb_assert (size
== 0);
810 /* Receive a message from the helper. Issue an assertion failure if
811 the message isn't a correctly-formatted MNSH_RET_FD. Set FD and
812 ERROR and return 0 on success. Set errno and return -1 on
816 mnsh_recv_fd (struct linux_mnsh
*helper
, int *fd
, int *error
)
818 enum mnsh_msg_type type
;
823 size
= mnsh_recv_message (helper
->sock
, &type
, fd
,
829 gdb_assert (type
== MNSH_RET_FD
);
830 gdb_assert (size
== 0);
834 gdb_assert (result
< 0);
841 /* Receive a message from the helper. Issue an assertion failure if
842 the message isn't a correctly-formatted MNSH_RET_INTSTR. Set
843 RESULT and ERROR and optionally store data in BUF, then return
844 the number of bytes stored in BUF on success (this may be zero).
845 Set errno and return -1 on error. */
848 mnsh_recv_intstr (struct linux_mnsh
*helper
,
849 int *result
, int *error
,
850 void *buf
, int bufsiz
)
852 enum mnsh_msg_type type
;
856 size
= mnsh_recv_message (helper
->sock
, &type
, &fd
,
863 gdb_assert (type
== MNSH_RET_INTSTR
);
864 gdb_assert (fd
== -1);
869 /* Return values for linux_mntns_access_fs. */
873 /* Something went wrong, errno is set. */
876 /* The main process is in the correct mount namespace.
877 The caller should access the filesystem directly. */
880 /* The helper is in the correct mount namespace.
881 The caller should access the filesystem via the helper. */
885 /* Return a value indicating how the caller should access the
886 mount namespace of process PID. */
888 static enum mnsh_fs_code
889 linux_mntns_access_fs (pid_t pid
)
893 struct linux_mnsh
*helper
;
897 if (pid
== getpid ())
898 return MNSH_FS_DIRECT
;
900 ns
= linux_ns_get_namespace (LINUX_NS_MNT
);
902 return MNSH_FS_DIRECT
;
904 fd
= gdb_open_cloexec (linux_ns_filename (ns
, pid
), O_RDONLY
, 0);
906 return MNSH_FS_ERROR
;
910 int save_errno
= errno
;
915 if (fstat (fd
, &sb
) != 0)
916 return MNSH_FS_ERROR
;
918 if (sb
.st_ino
== ns
->id
)
919 return MNSH_FS_DIRECT
;
921 helper
= linux_mntns_get_helper ();
923 return MNSH_FS_ERROR
;
925 if (sb
.st_ino
!= helper
->nsid
)
929 size
= mnsh_send_setns (helper
, fd
, 0);
931 return MNSH_FS_ERROR
;
933 if (mnsh_recv_int (helper
, &result
, &error
) != 0)
934 return MNSH_FS_ERROR
;
938 /* ENOSYS indicates that an entire function is unsupported
939 (it's not appropriate for our versions of open/unlink/
940 readlink to sometimes return with ENOSYS depending on how
941 they're called) so we convert ENOSYS to ENOTSUP if setns
947 return MNSH_FS_ERROR
;
950 helper
->nsid
= sb
.st_ino
;
953 return MNSH_FS_HELPER
;
956 /* See nat/linux-namespaces.h. */
959 linux_mntns_open_cloexec (pid_t pid
, const char *filename
,
960 int flags
, mode_t mode
)
962 enum mnsh_fs_code access
= linux_mntns_access_fs (pid
);
963 struct linux_mnsh
*helper
;
967 if (access
== MNSH_FS_ERROR
)
970 if (access
== MNSH_FS_DIRECT
)
971 return gdb_open_cloexec (filename
, flags
, mode
);
973 gdb_assert (access
== MNSH_FS_HELPER
);
975 helper
= linux_mntns_get_helper ();
977 size
= mnsh_send_open (helper
, filename
, flags
, mode
);
981 if (mnsh_recv_fd (helper
, &fd
, &error
) != 0)
990 /* See nat/linux-namespaces.h. */
993 linux_mntns_unlink (pid_t pid
, const char *filename
)
995 enum mnsh_fs_code access
= linux_mntns_access_fs (pid
);
996 struct linux_mnsh
*helper
;
1000 if (access
== MNSH_FS_ERROR
)
1003 if (access
== MNSH_FS_DIRECT
)
1004 return unlink (filename
);
1006 gdb_assert (access
== MNSH_FS_HELPER
);
1008 helper
= linux_mntns_get_helper ();
1010 size
= mnsh_send_unlink (helper
, filename
);
1014 if (mnsh_recv_int (helper
, &ret
, &error
) != 0)
1023 /* See nat/linux-namespaces.h. */
1026 linux_mntns_readlink (pid_t pid
, const char *filename
,
1027 char *buf
, size_t bufsiz
)
1029 enum mnsh_fs_code access
= linux_mntns_access_fs (pid
);
1030 struct linux_mnsh
*helper
;
1034 if (access
== MNSH_FS_ERROR
)
1037 if (access
== MNSH_FS_DIRECT
)
1038 return readlink (filename
, buf
, bufsiz
);
1040 gdb_assert (access
== MNSH_FS_HELPER
);
1042 helper
= linux_mntns_get_helper ();
1044 size
= mnsh_send_readlink (helper
, filename
);
1048 size
= mnsh_recv_intstr (helper
, &ret
, &error
, buf
, bufsiz
);
1056 gdb_assert (size
== ret
);
This page took 0.051931 seconds and 4 git commands to generate.