Utilizo una distribución basada en Linux 4.x, y recientemente me di cuenta de que la open()
llamada al sistema del núcleo admite una O_PATH
bandera abierta.
Si bien la man
página tiene una lista de llamadas al sistema con las que teóricamente podría usarse, no entiendo muy bien cuál es la idea. ¿ open(O_PATH)
Solo hago directorios, en lugar de archivos? Y si lo hago, ¿por qué quiero usar un descriptor de archivo en lugar de la ruta del directorio? Además, la mayoría de las llamadas al sistema enumeradas allí no parecen ser particulares de los directorios; entonces, ¿también abro archivos normales con O_PATH
para obtener de alguna manera su directorio como descriptor de archivo? ¿O para obtener un descriptor de archivo para ellos pero con una funcionalidad limitada?
¿Alguien puede dar una explicación convincente de qué O_PATH
se trata, cómo y para qué se supone que debemos usarlo?
Notas:
- No es necesario describir la historia de cómo evolucionó esto (las páginas de manual relevantes mencionan los cambios en Linux 2.6.x, 3.5 y 3.6) a menos que sea necesario, solo me importa cómo están las cosas ahora.
- Por favor, no me digas que solo use libc u otras instalaciones de nivel superior, lo sé.
fuente
Respuestas:
La descripción en la
open(2)
página del manual ofrece algunas pistas para comenzar:A veces, no queremos abrir un archivo o un directorio. En cambio, solo queremos una referencia a ese objeto del sistema de archivos para realizar ciertas operaciones (por ejemplo,
fchdir()
a un directorio al que hace referencia un descriptor de archivo que abrimos usandoO_PATH
). Entonces, un punto trivial: si este es nuestro propósito, entonces abrir conO_PATH
debería ser un poco más barato, ya que el archivo en sí no está realmente abierto.Y un punto menos trivial: antes de la existencia de
O_PATH
, la forma de obtener dicha referencia a un objeto del sistema de archivos era abrir el objeto conO_RDONLY
. Pero el uso deO_RDONLY
requiere que tengamos permiso de lectura sobre el objeto. Sin embargo, hay varios casos de uso en los que no necesitamos leer realmente el objeto: por ejemplo, ejecutar un binario o acceder a un directorio (fchdir()
) o alcanzar un directorio para tocar un objeto dentro del directorio.Uso con llamadas al sistema "* at ()"
El común, pero no el único, de uso
O_PATH
es abrir un directorio, con el fin de tener una referencia a ese directorio para su uso con el "*" en las llamadas al sistema, tales comoopenat()
,fstatat()
,fchownat()
, y así sucesivamente. Esta familia de las llamadas al sistema, que se nos ocurren más o menos de que los sucesores modernos a las llamadas al sistema de mayor edad con nombres similares (open()
,fstat()
,fchown()
, etc.), sirven un par de propósitos, el primero de los cuales se toca en cuando se pregunta " ¿por qué quiero usar un descriptor de archivo en lugar de la ruta del directorio? ". Si miramos más abajo en laopen(2)
página del manual, encontramos este texto (bajo un subtítulo con la justificación de las llamadas al sistema "* at"):Para hacer esto más concreto ... Supongamos que tenemos un programa que desea realizar múltiples operaciones en un directorio que no sea su directorio de trabajo actual, lo que significa que debemos especificar algún prefijo de directorio como parte de los nombres de archivo que usamos. Supongamos, por ejemplo, que la ruta es
/dir1/dir2/file
y queremos realizar dos operaciones:/dir1/dir2/file
(por ejemplo, a quién pertenece el archivo o a qué hora se modificó por última vez)./dir1/dir2/file.new
.Ahora, primero supongamos que hicimos todo usando llamadas de sistema tradicionales basadas en el nombre de ruta:
Ahora, además, suponga que en el prefijo de directorio
/dir1/dir2
uno de los componentes (por ejemplodir2
) era en realidad un enlace simbólico (que se refiere a un directorio), y que entre la llamadastat()
y la llamada aopen()
una persona malintencionada pudo cambiar el objetivo del enlace simbólicodir2
para apuntar a un directorio diferente. Esta es una condición clásica de carrera de tiempo de verificación y tiempo de uso. Nuestro programa verificó un archivo en un directorio pero luego fue engañado para crear un archivo en un directorio diferente, quizás un directorio sensible a la seguridad. El punto clave aquí es que el nombre de ruta se/dir/dir2
veía igual, pero lo que se refiere cambió por completo.Podemos evitar este tipo de problemas usando las llamadas "* at". En primer lugar, obtenemos un identificador que hace referencia al directorio donde haremos nuestro trabajo:
El punto crítico aquí es que
dirfd
es una referencia estable al directorio al que hacía referencia la ruta/dir1/dir2
en el momento de laopen()
llamada. Si el objetivo del enlace simbólicodir2
se modifica posteriormente, esto no afectará a lo que sedirfd
refiere. Ahora, podemos hacer nuestra operación de verificación + usando las llamadas "* at" que son equivalentes a las llamadasstat()
yopen()
anteriores:Durante estos pasos, cualquier manipulación de enlaces simbólicos en el nombre de ruta
/dir/dir2
no tendrá ningún impacto: se garantiza que la verificación (fstatat()
) y la operación (openat()
) se realicen en el mismo directorio.Hay otro propósito para usar las llamadas "* at ()", que se relaciona con la idea de "directorios de trabajo actuales por subproceso" en programas multiproceso (y nuevamente podríamos abrir los directorios usando
O_PATH
), pero creo que este uso es probablemente menos relevante para su pregunta, y le dejo que lea laopen(2)
página del manual si desea obtener más información.Uso con descriptores de archivos para archivos normales
Un uso de
O_PATH
con archivos normales es abrir un archivo binario para el que tenemos permiso de ejecución (pero no necesariamente permiso de lectura, para que no podamos abrir el archivo conO_RDONLY
). Ese descriptor de archivo se puede pasar afexecve(3)
para ejecutar el programa. Todo lo quefexecve(fd, argv, envp)
está haciendo con sufd
argumento es esencialmente:(Aunque, comenzando con glibc 2.27, la implementación utilizará la
execveat(2)
llamada del sistema, en los núcleos que proporcionan esa llamada del sistema).fuente
The problem is that between the existence check and the file creation step, path or to ... could be modified
- No puedo analizar esta oración. Pero entiendo lo esencial, creo. Por lo tanto, sirve como una especie de mecanismo de bloqueo en un directorio. Pero, ¿por qué usar elopen()
resultado en lugar de un bloqueo real?