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.