Directorios y Ficheros en C – Linux

| 2013-08-29 | No hay comentarios »

Para terminar con las funciones relacionadas con el manejo de ficheros veremos chmod(), chown() , stat() , readdir(), opendir(), scandir().

La función chmod() tiene el mismo uso que el comando del mismo nombre: cambiar los modos de acceso permitidos para un fichero en concreto. Por mucho que estemos utilizando C, nuestro programa sigue sujeto a las restricciones del Sistema de Ficheros, y sólo su propietario o root podrán cambiar los modos de acceso a un fichero determinado. Al crear un fichero, bien con creat() o bien con open(), éste tiene un modo que estará en función de la máscara de modos que esté configurada (ver “man umask”), pero podremos cambiar sus modos inmediatamente haciendo uso de una de estas funciones:

int chmod(const char *path, mode_t mode);
int fchmod(int fildes, mode_t mode);

Viendo el prototipo de cada función, podemos averiguar su funcionamiento: la primera de ellas, chmod(), modifica el modo del fichero indicado en la cadena “path”. La segunda, fchmod(), recibe un descriptor de fichero, “fildes”, en lugar de la cadena de caracteres con la ruta al fichero. El parámetro “mode” es de tipo “mode_t”, pero en GNU/Linux es equivalente a usar una variable de tipo entero. Su valor es exactamente el mismo que el que usaríamos al llamar al comando “chmod”, por ejemplo:

chmod( “/home/txipi/prueba”, 0666 );

Para modificar el propietario del fichero usaremos las siguientes funciones:

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);

Con ellas podremos cambiar el propietario y el grupo de un fichero en función de su ruta ( chown() y lchown() ) y en función del descriptor de fichero ( fchown() ). El propietario (“owner”) y el grupo (“group”) son enteros que identifican a los usuarios y grupos, tal y como especifican los ficheros “/etc/passwd” y “/etc/group”. Si fijamos alguno de esos dos parámetros (“owner” o “group”) con el valor –1, se entenderá que deseamos que permanezca como estaba. La función lchown() es idéntica a chown() salvo en el tratamiento de enlaces simbólicos a ficheros. En versiones de Linux anteriores a 2.1.81 (y distintas de 2.1.46), chown() no seguía enlaces simbólicos. Fue a partir de Linux 2.1.81 cuando chown() comenzó a seguir enlaces simbólicos y se creó una nueva syscall, lchown(), que no seguía enlaces simbólicos. Por lo tanto, si queremos aumentar la seguridad de nuestros programas, emplearemos lchown(), para evitar malentendidos con enlaces simbólicos confusos.

Cuando el propietario de un fichero ejecutable es modificado por un usuario normal (no root), los bits de SUID y SGID se deshabilitan. El estándar POSIX no especifica claramente si esto debería ocurrir también cuando root realiza la misma acción, y el comportamiento de Linux depende de la versión del kernel que se esté empleando. Un ejemplo de su uso podría ser el siguiente:

gid_t grupo = 100; /* 100 es el GID del grupo users */

chown( “/home/txipi/prueba”, -1, grupo);

Con esta llamada estamos indicando que queremos modificar el propietario y grupo del fichero “/home/txipi/prueba”, dejando el propietario como estaba (-1), y modificando el grupo con el valor 100, que corresponde al grupo “users”:

txipi@neon:~$ grep 100 /etc/group
users:x:100:

La función stat(): Esta función tiene un comportamiento algo diferente a lo visto hasta ahora: utiliza una estructura de datos con todas las características posibles de un fichero, y cuando se llama a stat() se pasa una referencia a una estructura de este tipo. Al final de la syscall, tendremos en esa estructura todas las características del fichero debidamente cumplimentadas. Las funciones relacionadas con esto son las siguientes:

int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *file_name, struct stat *buf);

Es decir, muy similares a chown(), fchown() y lchown(), pero en lugar de precisar los propietarios del fichero, necesitan como segundo parámetro un puntero a una estructura de tipo “stat”:

struct stat {
  dev_t         st_dev;      /* dispositivo */
  ino_t         st_ino;      /* numero de inode */
  mode_t        st_mode;     /* modo del fichero */
  nlink_t       st_nlink;    /* numero de hard links */
  uid_t         st_uid;      /* UID del propietario*/
  gid_t         st_gid;      /* GID del propietario */
  dev_t         st_rdev;     /* tipo del dispositivo */
  off_t         st_size;     /* tamaño total en bytes */
  blksize_t     st_blksize;  /* tamaño de bloque preferido */
  blkcnt_t      st_blocks;   /* numero de bloques asignados */
  time_t        st_atime;    /* ultima hora de acceso */
  time_t        st_mtime;    /* ultima hora de modificación */
  time_t        st_ctime; /* ultima hora de cambio en inodo */
};

También tenemos la estructura dirent:

struct dirent {
    ino_t          d_ino;       /* inode number */
    off_t          d_off;       /* offset to the next dirent */
    unsigned short d_reclen;    /* length of this record */
    unsigned char  d_type;      /* type of file; not supported
                                   by all file system types */
    char           d_name[256]; /* filename */
};

Para utilizar esa estructura debemos de incorporar la librería dirent.h a nuestro programa. Los prototipos de las funciones de la librería son las siguientes:

int            closedir(DIR *);
DIR           *opendir(const char *);
struct dirent *readdir(DIR *);
int            readdir_r(DIR *, struct dirent *, struct dirent **);
void           rewinddir(DIR *);
void           seekdir(DIR *, long int);
long int       telldir(DIR *);

Como ejemplo: Abrir un directorio ingresado por teclado, mostrar los permisos, nombre y tamaño de los ficheros de dicho directorio.


//Llamada a librerías
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>

//Declaramos los prototipos de funciones
void imprimir (struct stat estru);

//Función principal
main(int argc, char **argv[])
{
//Declaramos variables, estructuras
struct stat estru;
struct dirent *dt;
DIR *dire;
char *dir;
int bytes=0;

system("clear");

//Ingresamos por teclado el directorio
scanf("%s",&dir);

//dire=opendir(".");
//Para utilizar la función open dir, incluir la libreria dirent.h
//La función lo que hace es abrir un directorio
dire=opendir(&dir);

//Leer el directorio y recorrerlo
//La función readdir, lee el directorio completo
while((dt=readdir(dire)) != NULL){
 //Con la función stat podemos ver el estado de los ficheros
 //En este ejemplo, se muestra los permisos, nombre, y tamaño
 stat(dt->d_name, &estru);
 imprimir(estru);
 printf("%-20s %d \n",dt->d_name,estru.st_size);
 bytes=bytes+estru.st_size;
}
bytes=bytes/1024;
printf("\nTotal en KB: %d",bytes);
closedir(dire);

}

//Función que permite imprimir los permisos
void imprimir (struct stat estru){
 printf( (S_ISDIR(estru.st_mode)) ? "d" : "-" );
 printf( (estru.st_mode & S_IRUSR) ? "r" : "-" );
 printf( (estru.st_mode & S_IWUSR) ? "w" : "-" );
 printf( (estru.st_mode & S_IXUSR) ? "x" : "-" );
 printf( (estru.st_mode & S_IRGRP) ? "r" : "-" );
 printf( (estru.st_mode & S_IWGRP) ? "w" : "-" );
 printf( (estru.st_mode & S_IXGRP) ? "x" : "-" );
 printf( (estru.st_mode & S_IROTH) ? "r" : "-" );
 printf( (estru.st_mode & S_IWOTH) ? "w" : "-" );
 printf( (estru.st_mode & S_IXOTH) ? "x" : "-" );
 printf("%-3s");
}

También tenemos otra alternativa que es utilizar scandir() que trae la librería dirent.h:

int scandir(const char *dir, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*compar)(const struct dirent **, const struct dirent **));
int alphasort(const struct dirent **a, const struct dirent **b);

scandir () explora el directorio.
alphasort () se pueden utilizar como la comparación de la función scandir () para ordenar el directorio en orden alfabético.


//El programa nos permite mostrar los archivos del directorio actual pero en orden inverso
//Llamada a librerías
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>

//Función principal
main(int argc, char **argv[])
{
//Declaramos variables, estructuras
//namelist lleva dos asteriscos porque apunta a varios dirents
 struct dirent **namelist;

 int n;
 //En esta variable se guarda el número de archivos del directorio
 //Posteriormente lo que se hace es recorrer el diectorio con un while
 n = scandir(".", &namelist, 0, alphasort);
 if (n < 0)
 perror("scandir");
 else {
 while(n--) {
 //Mostramos en pantalla los nombres de los archivos
 printf("%s\n", namelist[n]->d_name);
 //Liberamos memoria
 free(namelist[n]);
 }
 //Liberamos memoria
 free(namelist);
 }
}

Por último, dejo como ejemplo un programa que lista un directorio de forma re-cursiva, es algo parecido a hacer “ls -lR .” en la consola:


//Llamada a librerías
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

//Declaramos los prototipos de funciones
void imprime_permisos(struct stat estru);
void lista_directorio(char *nombre);
//Función principal
main(int argc, char **argv)
{
 //Limpiamos la pantalla
 system("clear");

 //lista_directorio(argv[1]);
 //Llamamos a la función
 lista_directorio(".");

}

//Función que permite listar un directorio de manera recursiva
void lista_directorio(char *nombre){

//Declaramos variables, estructuras
 struct stat estru;
 struct dirent *dt;
 DIR *dire;

 dire = opendir(nombre);

 printf("abriendo el directorio %s\n",nombre);
 //Recorrer directorio
 while((dt=readdir(dire))!=NULL){
 //strcmp permite comparar, si la comparación es verdadera devuelve un 0
 //Aquí se pregunta si el arhivo o directorio es distinto de . y ..
 //Para así asegurar que se muestre de forma recursiva los directorios y ficheros del directorio actual
 if((strcmp(dt->d_name,".")!=0)&&(strcmp(dt->d_name,"..")!=0)){
 stat(dt->d_name,&estru);
 //Si es un directorio, llamar a la misma función para mostrar archivos
 if(S_ISDIR(estru.st_mode)){
 lista_directorio(dt->d_name);
 //Si no es directorio, mostrar archivos
 }else{
 imprime_permisos(estru);
 printf("%-20s %d \n",dt->d_name,estru.st_size);
 }
 }

 }
 closedir(dire);

}

//Función que permite imprimir permisos
void imprime_permisos(struct stat estru){
 printf( (S_ISDIR(estru.st_mode)) ? "d" : "-");
 printf( (estru.st_mode & S_IRUSR) ? "r" : "-");
 printf( (estru.st_mode & S_IWUSR) ? "w" : "-");
 printf( (estru.st_mode & S_IXUSR) ? "x" : "-");
 printf( (estru.st_mode & S_IRGRP) ? "r" : "-");
 printf( (estru.st_mode & S_IWGRP) ? "w" : "-");
 printf( (estru.st_mode & S_IXGRP) ? "x" : "-");
 printf( (estru.st_mode & S_IROTH) ? "r" : "-");
 printf( (estru.st_mode & S_IWOTH) ? "w" : "-");
 printf( (estru.st_mode & S_IXOTH) ? "x" : "-");
 printf("%-3s");
}

Parte del artículo está basado en este artículo.

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Developers SO Sistemas Operativos preferidos por los developers
  • Instalar Tomcat 7 en CentOS 6
  • RPC (Remote Procedure Call) en C – Linux
  • Sockets en C (Parte II) – Linux