HOWTO · C

Crear un nuevo directorio en C

Este artículo presenta cómo crear un nuevo directorio en C

En esta página

Use la función POSIX mkdir para crear un directorio directamente desde C. Recibe una ruta y bits de permisos, y comunica los errores mediante el valor de retorno y errno. La máscara umask del proceso filtra los permisos solicitados, por lo que el modo resultante puede ser más restrictivo que el pasado a la llamada. Si una ruta debe interpretarse respecto de un directorio abierto concreto y no del directorio de trabajo del proceso, mkdirat permite usar ese patrón.

Crear un directorio con mkdir

Incluya <sys/stat.h> para usar mkdir. El primer argumento es la ruta del nuevo directorio; el segundo es un modo, como 0750. Con ese modo, el propietario puede leer, escribir y buscar en el directorio, mientras que los miembros del grupo pueden leer y buscar en él. Los demás usuarios no reciben permisos de estos bits solicitados. La máscara umask del proceso puede quitar permisos, pero no añadirlos.

Ejecute este ejemplo:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
    char parent[] = "/tmp/mkdir-demo-XXXXXX";
    char path[sizeof(parent) + sizeof("/reports")];

    if (mkdtemp(parent) == NULL) {
        perror("mkdtemp");
        return EXIT_FAILURE;
    }
    int path_length = snprintf(path, sizeof(path), "%s/reports", parent);
    if (path_length < 0 || (size_t)path_length >= sizeof(path)) {
        puts("path formatting failed");
        if (rmdir(parent) == -1) {
            perror("rmdir parent");
        }
        return EXIT_FAILURE;
    }

    if (mkdir(path, 0750) == -1) {
        perror("mkdir");
        if (rmdir(parent) == -1) {
            perror("rmdir parent");
        }
        return EXIT_FAILURE;
    }

    puts("Created reports directory.");
    int result = EXIT_SUCCESS;
    if (rmdir(path) == -1) {
        perror("rmdir child");
        result = EXIT_FAILURE;
    }
    if (rmdir(parent) == -1) {
        perror("rmdir parent");
        result = EXIT_FAILURE;
    }
    return result;
}
Created reports directory.

El registro de ejecución suministrado muestra que el caso correcto imprime Created reports directory.. Los casos de error no aparecen en ese registro. El ejemplo crea un directorio principal temporal, crea reports dentro de él, imprime el mensaje de éxito y elimina ambos directorios. En su aplicación, sustituya la ruta temporal por la ubicación que necesite y decida si los permisos elegidos son adecuados para los datos que almacenará allí.

Gestionar errores y rutas existentes

mkdir devuelve 0 si tiene éxito y -1 si falla. En caso de error, establece errno; llame a perror cuanto antes o guarde errno antes de realizar otras operaciones que puedan cambiarlo. Entre las causas habituales están que no exista el directorio principal, que falten permisos para modificarlo, que la ruta supere el límite del sistema o que uno de sus componentes no sea un directorio. Si el destino ya existe, mkdir también falla con EEXIST, tanto si se trata de un directorio como de otro tipo de archivo.

Considere autoritativo el resultado de la creación. Comprobar con stat antes de llamar a mkdir no garantiza que la ruta siga sin existir hasta el momento de crearla: otro proceso puede crearla entre la comprobación y la llamada. Si su programa acepta como éxito que ya exista un directorio, gestione EEXIST y después inspeccione la ruta con stat o lstat para confirmar su tipo, teniendo en cuenta las condiciones de carrera si otro proceso puede cambiarla al mismo tiempo. No acepte silenciosamente un archivo normal existente.

El argumento 0750 es un modo solicitado, no necesariamente el modo final. Por ejemplo, una máscara umask restrictiva puede quitar bits del grupo o del propietario. Si necesita permisos exactos, compruebe el modo resultante del directorio y aplique una política de permisos deliberada después de crearlo; tenga en cuenta las implicaciones de seguridad de cambiar los permisos de una ruta que otro proceso podría haber reemplazado.

Crear un directorio respecto de un directorio abierto con mkdirat

mkdirat recibe un descriptor de archivo de directorio, una ruta y un modo. Si la ruta es relativa, el núcleo la resuelve desde ese descriptor, independientemente de los cambios posteriores en el directorio de trabajo del proceso. Esto resulta útil cuando una aplicación ya tiene abierto un directorio y quiere anclar allí las operaciones posteriores. Una ruta absoluta no se interpreta respecto del descriptor.

El ejemplo abre un directorio principal temporal, crea un directorio hijo respecto de su descriptor y cierra el descriptor. Después elimina el directorio temporal. El registro de ejecución suministrado muestra que el caso correcto termina sin salida estándar; no informa de ejecuciones de casos de error.

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
    char parent[] = "/tmp/mkdirat-demo-XXXXXX";
    char child[sizeof(parent) + sizeof("/reports")];
    int dirfd;
    int result = EXIT_FAILURE;
    int created = 0;

    if (mkdtemp(parent) == NULL) {
        perror("mkdtemp");
        return EXIT_FAILURE;
    }
    dirfd = open(parent, O_RDONLY);
    if (dirfd == -1) {
        perror("open");
        if (rmdir(parent) == -1) {
            perror("rmdir parent");
        }
        return EXIT_FAILURE;
    }

    if (mkdirat(dirfd, "reports", 0750) == -1) {
        perror("mkdirat");
    } else {
        created = 1;
        result = EXIT_SUCCESS;
    }

    if (close(dirfd) == -1) {
        perror("close");
        result = EXIT_FAILURE;
    }
    if (created) {
        int child_length = snprintf(child, sizeof(child), "%s/reports", parent);
        if (child_length < 0 || (size_t)child_length >= sizeof(child)) {
            puts("path formatting failed");
            result = EXIT_FAILURE;
        } else {
            if (rmdir(child) == -1) {
                perror("rmdir child");
                result = EXIT_FAILURE;
            }
        }
    }
    if (rmdir(parent) == -1) {
        perror("rmdir parent");
        result = EXIT_FAILURE;
    }
    return result;
}

Mantenga abierto el descriptor mientras lo necesite y ciérrelo en todas las rutas de ejecución. En código de producción, asegúrese de que la limpieza elimine solo los objetos creados por su programa. Para operaciones sensibles desde el punto de vista de la seguridad, considere abrir el directorio con opciones adecuadas para su plataforma y validar el directorio que pretende usar; mkdirat ancla la resolución de rutas relativas, pero por sí sola no valida todos los componentes de una ruta suministrada.

Por qué evitar system() para crear directorios

En general, no hace falta llamar a system() para ejecutar un comando del shell cuando C ya proporciona mkdir. Esto añade el inicio del shell y la interpretación del comando; además, construir una cadena de comando a partir de una ruta puede permitir que los metacaracteres del shell en una ruta no confiable alteren lo que se ejecuta. Es difícil entrecomillar correctamente las rutas en distintos shells. Llame directamente a mkdir o mkdirat, compruebe el resultado e informe de errno. El shell solo es apropiado cuando la aplicación necesita deliberadamente su comportamiento y gestiona la construcción y ejecución del comando mediante una política de seguridad independiente y cuidadosamente diseñada.