10
Ficheiros e Directorias
Sumário:
|
Introdução
Há vários utilitários
do Unix para aceder e manipular ficheiros e directorias, tais
como, por exemplo, ls, mkdir,
cp, rm, chmod
etc. Nesta
subsecção vamos ver como fazer o mesmo a partir dum programa escrito em
linguagem C. Esta programação requer a inclusão dos ficheiros de protótipos
<unistd.h>, <sys/types.h> e <sys/stat.h>.
Nome de ficheiro
Qualquer ficheiro,
directoria ou ficheiro especial tem um nome (filename).
Contudo, há dois
caracteres ASCII que não são permitidos no nome dum ficheiro: o carácter ´\0´
(ou NULL) e o carácter ´/´. O carácter ´/´ é o separador de nomes de ficheiros
no nome dum caminho (ou pathname) do sistema
de ficheiros, ao passo que o carácter ´\0´ é o carácter terminador duma string, e, por consequência, do nome dum caminho.
Os ficheiros são
guardados numa estrutura logica chamada, directoria. As
directorias são organizado numa estrutura hierárquica tipo arvore. Em
Unix a directoria inicial, ou directoria raiz do sistema é a directoria
indicado apenas pelo ‘/’.
Nome de caminho
Um caminho é uma string construída por um ou mais nomes de ficheiros
separados em Unix pelo carácter ´/´. Outros sistemas operativos podem usar
outro caracter de seperação, Windows por exempl,o utilize o simbolo ‘\’.
Em Unix um caminho é absoluto
se começa com o carácter ´/´; caso contrário, o caminho é relativo.
Há uma diferença
entre o nome dum ficheiro e o nome dum caminho. O nome dum ficheiro é um
componente dum nome dum caminho. O nome dum caminho é uma string
que é passada dum processo para o kernel
quando um ficheiro específico é referenciado. No entanto, muitos utilizadores e
autores de livros referem-se a strings como, por
exemplo, /usr/lib/crontab como se fosse um nome dum ficheiro, quando na
realidade é o nome dum caminho.
Descritor de ficheiro
Um descritor de
ficheiro é um número inteiro que é usado para identificar um ficheiro aberto
para I/O. Os actuais sistemas operativos já permitem que haja um numero
significante de ficheiros abertos por processo. Esta informação fica guardada
no PCB (process control block) de cada processo. Normalmente, os descritores de
ficheiros 0, 1 e 2 estão associados à entrada estandardizada, à saída
estandardizada e ao erro estandardizado, respectivamente, dum processo.
O kernel atribui descritores de ficheiros aquando das
seguintes chamadas ao sistema bem-sucedidas: open,
creat, dup, pipe, e fcntl.
Note-se que os sockets também tem descritores
inteiros. O kernel atribui
descritores a sockets quando as seguintes chamadas ao
sistema são bem-sucedidas: accept, pipe, socket,
e socketpair.
Estado dum
ficheiro
Um ficheiro tem
atributos e estes atributos constituem o estado do dito ficheiro e representam
informações e características dos ficheiros
Há duas funções que
permitem saber o estado dum ficheiro.
§ Obtém informação
sobre o ficheiro identificado por path.
int stat(char *path, struct stat *buf)
§
Obtém
informação sobre o ficheiro identificado pelo descritor fd.
int fstat(int fd, struct stat *buf)
O argumento buf é um ponteiro para
uma estrutura stat na qual está colocada a informação respeitante ao ficheiro.
Ambas as funções devolvem ou o valor 0 ou o valor –1,
consoante há sucesso ou erro na chamada da função. A estrutura stat está definida em <sys/types.h> como se segue:
mode_t st_mode; /* file mode
(type, permissions) */
ino_t st_ino; /* i-node number */
dev_t st_dev; /* ID of device containing a directory entry
for this file */
dev_t st_rdev; /* ID of device defined only for char special
or block special files */
short st_nlink; /* number of links */
uid_t st_uid; /* user ID of the file’ s
owner */
gid_t st_gid; /* group ID of the file’s group */
off_t st_size; /* file size in bytes */
time_t st_atime; /* time of the last access */
time_t st_mtime; /* time of the last data modification */
time_t st_ctime; /* time of the last file status change */
long st_blksize; /* optimal I/O block size for filesystem operations*/
long st_blocks; /* actual number of 512 byte blocks allocated
*/
}
A componente st_mode da estrutura stat
contém o tipo de ficheiro, as permissões de acesso (9 bits) e outras permissões
especiais tais como o set-user-ID bit, o set-group-ID bit e o sticky bit.
Por exemplo se o tipo
mode_t é representado por
um unsigned short int
de 16 bits então o tipo de ficheiro será representado usando os primeiros 3
bits , as permissões de acesso os seguintes 9 bits e as outras permissões os 4
bits restantes.
O tipo certo de mode_t pode variar conforme o sistema operativo e
arquitectura. Pode ser encontrado através dos ficheiro de inclusão, /usr/include/sys/types.h etc. Normalmente
obriga algum trabalho de pesquisa !
Tipos de
ficheiros
O tipo de ficheiro
codificado em st_mode pode ser um dos
seguintes:
·
Ficheiro regular ou ordinário. É o tipo mais comum de ficheiro, o
qual contém dados sob alguma forma. O sistema UNIX não faz distinção entre
dados textuais e dados binários. Qualquer interpretação do conteúdo dum
ficheiro ordinário é deixado para as aplicações.
·
Directoria. Contém os nomes e
os i-nodes doutros ficheiros (Um i-node é uma estrutura de dados em disco que
contém informação acerca dum ficheiro como, por exemplo, a sua localização em
disco.) Qualquer processo que tem permissão de leitura duma directoria pode ler
o seu conteúdo, mas só o kernel
pode escrever nela.
·
Ficheiro especial de caracteres. Este tipo de
ficheiro é usado para certos tipos de dispositivos I/O.
·
Ficheiro especial de blocos. É um tipo de
ficheiro usado para dispositivos do tipo bloco, por exemplo o disco rígido.
Todos os dispositivos I/O são ficheiros especiais de caracteres ou de blocos, e
que aparecem como ficheiros ordinários no sistema UNIX. A manipulação destes
ficheiros depende do dispositivo real que está a ser referenciado.
·
Fifo. É uma fila de
espera first-in first-out,
também conhecida por pipeta com nome (named
pipe) que é usada para comunicação entre
processos (interprocess communication
ou IPC).
·
Elo simbólico (symbolic
link). É um ficheiro que contém o caminho doutro ficheiro.
·
Socket. É um ficheiro especial
usado em comunicação entre processos em rede.
Nota
:
No comando de bash shell “ls –l “estes tipos são indicados
através das seguintes letras (- regular, d directory
b block, c character c, l
link, p pipe, s socket)
É possível determinar
o tipo dum ficheiro através da utilização
das seguintes máscaras definidas em <sys/stat.h>:
#define
S_IFDIR 0040000 /* directory */
#define
S_IFCHR 0020000 /* character special */
#define
S_IFBLK 0060000 /*
block special */
#define
S_IFREG 0100000 /* regular */
#define
S_IFLNK 0120000 /*
symbolic link */
#define
S_IFSOCK 0140000 /* socket */
#define
S_IFIFO 0010000 /* fifo
*/
Por exemplo
struct stat var;
stat( “/etc/passwd”,
&var)
if ( (var.st_mode & S_IFREG)
== S_IFREG )
printf(“/etc/passwd
Um ficheiro regular\n”);
else
printf(“/etc/passwd : Não é Um ficheiro regular\n”);
O tipo dum ficheiro pode ser
determinado directamente através das seguintes macros definidas em <sys/stat.h> onde o valor de st_mode
é combinado com a máscara genérica S_IFMT referente ao tipo de ficheiro.
#define S_IFMT 0170000 /* type of file */
#define
S_ISBLK(m)
(((m) & S_IFMT) == S_IFBLK)
#define
S_ISCHR(m)
(((m) & S_IFMT) == S_IFCHR)
#define
S_ISDIR(m)
(((m) & S_IFMT) == S_IFDIR)
#define
S_ISFIFO(m)
(((m) & S_IFMT) == S_IFIFO)
#define
S_ISREG(m)
(((m) & S_IFMT) == S_IFREG)
#define
S_ISLNK(m)
(((m) & S_IFMT) == S_IFLNK)
#define
S_ISSOCK(m)
(((m) & S_IFMT) == S_IFSOCK)
em que m é a
componente st_mode da estrutura stat. Note-se que
Exercício 10.1
Escreva uma função que identifique o tipo dum dado ficheiro. O nome do
ficheiro é passado por argumento. A seguir escreva um programa principal ou
integra esta função num outro programa e testar.
prompt> ftype /etc
/etc é uma directoria.
Permissões de
acesso a ficheiros
O valor de st_mode também codifica os bits ou permissões
de acesso a um ficheiro, qualquer que seja o seu tipo. Há 9 bits de permissão
de acesso a qualquer ficheiro divididos em 3 subconjuntos de 3 bits. O primeiro
subconjunto de 3 bits estabelece as permissões do proprietário do ficheiro. O segundo subconjunto
de 3 bits estabelece as permissões do grupo ao qual o proprietário pertence.
Por fim, o terceiro subconjunto de 3 bits estabelece as permissões dos
restantes utilizadores. Para cada subconjunto de 3 bits, o primeiro bit define
a permissão de leitura (r), o segundo bit define a permissão de escrita (w) e o
terceiro bit define a permissão de execução (x).
Naturalmente que também há 9 máscaras, cada uma das quais identifica uma
das 9 permissões. Além disso, cada subconjunto de 3 bits tem reservada uma
máscara.
#define
S_IRWXU 0000700 /* rwx, owner */
#define S_IRUSR 0000400 /* read permission, owner */
#define S_IWUSR 0000200 /* write permission, owner */
#define S_IXUSR 0000100 /* execute/search permission, owner
*/
#define
S_IRWXG 0000070 /* rwx, group */
#define S_IRGRP 0000040 /* read permission, group */
#define S_IWGRP 0000020 /* write permission, grougroup */
#define S_IXGRP 0000010 /* execute/search permission, group
*/
#define
S_IRWXO 0000007 /* rwx, other */
#define S_IROTH 0000004 /* read permission, other */
#define S_IWOTH 0000002 /* write permission, other */
#define S_IXOTH 0000001 /* execute/search permission, other
*/
Para determinar se um dado ficheiro tem uma dada permissão, há que fazer
também a conjunção lógica dos bits da máscara do subconjunto em causa com o
valor de st_mode. O resultado é naturalmente uma das nove permissões possíveis do
ficheiro.
Set-User-ID e Set-Group-ID
Todo o processo tem 6 ou mais IDs associados:
q Real user ID
q Real group ID
q Effective user
ID
q Effective
group ID
q Saved set-user ID
q Saved set-group ID
Os IDs reais identificam a quem pertence o processo na
realidade. Estes dois campos são extraídos do registo do utilizador no ficheiro
de passwords (/etc/passwd) quando o utilizador em causa dá entrada na máquina.
Normalmente, estes valores não mudam durante uma sessão, a não ser que um
processo do super-utilizador os altere.
Os IDs efectivos referem-se ao ficheiro executável
associado ao processo, i.e. determinam as permissões de acesso ao dito
ficheiro.
Os IDs salvaguardados contêm cópias dos IDs efectivos quando um
programa é executado.
Recorde-se que todo o ficheiro tem um proprietário (owner)
e um grupo (group), os quais são especificados pelas
componentes st_uid e st_gid da respectiva estrutura stat.
Normalmente, quando um ficheiro entra em execução fica associado a um
processo, o real user ID
e effective user ID são iguais, o
mesmo se passando com o real group
ID e o effective group ID. Esta
associação faz-se de modo que o effective user ID do processo
passa a ser o proprietário do ficheiro codificado na componente st_uid da estrutura stat do ficheiro. Esta associação só é concretizada após a activação do bit set-user-ID codificado na componente st_mode da estrutura stat. Do mesmo modo, o bit set-group-ID codificado
na componente st_mode da estrutura stat serve para associar o o effective group ID do processo ao
grupo do respectivo ficheiro codificado na componente st_gid da estrutura stat do ficheiro. Voltaremos a este assunto mais tarde.
Função umask
Agora que estão descritas os bits de permissão associados a cada ficheiro, podemos agora descrever a máscara de criação de file mode que está associado a todo o processo.
A função umask activa esta máscara para um processo e retorna o valor anterior.
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t cmask);
Retorna: o valor anterior da
máscara
O argumento cmask é formado por um OR
bit-a-bit de quaisquer das 9 permissões de leitura, de escrita e execução acima
indicadas. Uma máscara de criação de ficheiro é usada sempre que um ficheiro ou
directoria é criado. Recorde-se que as funções creat
e open aceitam o argumento mode
que especifica os bits das permissões de acesso a um novo ficheiro.
Quaisquer bits que estão on na máscara de
criação de modo do ficheiro são colocados a off no modo do ficheiro. Este é um mecanismo
de segurança permitindo assim a criação de ficheiro novos com a segurança que,
por exemplo, a permissão de execução ou permissão de leitura para outros não
serão ligados.
Exemplo 10.1
O programa seguinte cria dois ficheiros, um com umask
a 0 e o outro com valor de umask que inibe todas as
permissões de grupo e de outros utilizadores.
#include <sys/types.h> <sys/stat.h>
<fcntl.h> <stdio.h>
int main(void)
{
umask(0);
if (creat("foo",
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) < 0)
printf("creat error
for foo\n");
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (creat("bar", S_IRUSR | S_IWUSR | S_IRGRP |
S_IWGRP | S_IROTH | S_IWOTH) < 0)
printf("creat error
for bar\n");
return(0);
}
Output:
$ ./a.out
$ ls -l foo bar
-rw-rw-rw- 1 operativos disciplinas 0 2012-04-26 14:24 foo
-rw------- 1 operativos
disciplinas 0 2012-04-26 14:24 bar
A funcionalidade de alterar e ver
o valor do umask está implementado no bash shell. Ver o
exemplo que se segue:
$umask
0022
$ touch tmp1
$ umask 0000
$ umask
0000
$ touch tmp2
$ ls -l tmp?
-rw-r--r-- 1 operativos
disciplinas 0 2012-04-26 14:26 tmp1
-rw-rw-rw- 1 operativos disciplinas 0 2012-04-26 14:26 tmp2
Funções de
modificação de permissões de acesso: chmod e fchmod
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int filedes, mode_t mode);
Ambas retornam: 0 se OK, -1 em
caso de erro
Estas duas funções permitem mudar as permissões de acesso dum ficheiro:
§ int chmod(char *pathname,
mode_t mode)
Altera as permissões
de acesso a um ficheiro. O ficheiro é identificado pelo seu caminho (string) armazenado em path. O valor 0 é devolvido em caso de
sucesso, ao passo que o valor –1 é devolvido em caso
de erro.
§ int fchmod(int filedes, mode_t mode)
Altera as permissões
de acesso a um ficheiro aberto. O ficheiro é identificado pelo seu
descritor filedes. O valor 0 é devolvido em caso de
sucesso, ao passo que o valor –1 é devolvido em caso
de erro.
O parâmetro mode estabelece as permissões de acesso através de macros e
mascaras e existentes em <sys/stat.h>.
A função do Bash Shell, chmod, pode ser invocado
para alterar as permissões dum ficheiro através da atribuição de números octais
de 3 dígitos. O dígito à direita especifica os privilégios do proprietário (ou owner) do ficheiro, o dígito do meio especifica os
privilégios do grupo a que pertence o proprietário do ficheiro, e o dígito à
esquerda especifica os privilégios dos outros utilizadores. O equivalente binário de cada dígito
octal é um número binário de 3 bits. O bit à direita especifica o privilégio de
execução, o bit intermédio especifica o privilégio de escrita e o bit à
esquerda especifica o privilégio de leitura.
Por exemplo:
4 (octal 100) = read only;
2 (octal 010) = write only;
1 (octal 001) = execute only.
6 (octal 110) = uma combinação: read and
write;
Assim, o modo de
acesso 600 fornece ao proprietário os privilégios de read
e write, mas nenhuns privilégios a quaisquer outros
utilizadores, o acesso 660 dá privilégios de read e write ao proprietário e outros elementos do mesmo grupo de
utilizadores, e o modo de acesso 666 dá privilégios de read
e write a toda a gente.
Notas: Estas propriedades
também podem ser vistas e alteradas em ambiente gráfico.
A função chmod na realidade aceite 4 dígitos octais, o quarto
represente as permissões especiais.
Exemplo 10.2
Pretende-se activar a permissão de escrita e desligar a permissão de
execução de grupo do ficheiro foo.
Pretende-se ainda activar o modo absoluto “rw-r—r--” no ficheiro bar.
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void)
{
struct stat statbuf;
/* turn on
write-group and turn off group-execute */
if
(stat("foo", &statbuf) < 0)
printf("stat error for foo\n");
if (chmod("foo", (statbuf.st_mode
& ~S_IXGRP) | S_IWGRP) < 0)
printf("chmod error for
foo\n");
/* set absolute mode to "rw-r--r--"
*/
if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP |
S_IROTH) < 0)
printf("chmod
error for bar\n");
return(0);
}
Output:
$ touch
foo bar //O Comando touch pode ser usado para criara ficheiro vazios
$ chmod 650 bar foo
$ ls –l foo bar
-rw-r-x--- 1 crocker 0 May 2
16:00 foo
-rw-r-x--- 1 crocker 0 May 2
$ a.out
$ ls –l foo bar
-rw-rw---- 1 crocker 0 May
2 16:00 foo
-rw-r—r-- 1 crocker 0 May 2
$
Exercício 10.2
(i) Escreva um programa que adicione as permissões de execução dum dado
ficheiro.
(ii) Escreva um programa que retire as permissões de execução dum dado
ficheiro.
Requisito: o ficheiro deve ser previamente criado em disco.
Exercício 10.3
O que que dizer e para que servem os flags de “Sticky bit” e o “Group ID” ( S_ISGID
)
Funções de
modificação de proprietário e de grupo: chown, fchown e lchown
#include <sys/types.h>
#include <unistd.h>
int
chown(const char *pathname, uid_t
owner, gid_t group);
int
fchown(int filedes, uid_t owner, gid_t group);
int
lchown(const char *pathname, uid_t
owner, gid_t group);
Todas retornam: 0 se OK, -1 em
caso de erro
Estas funções permitem alterar o user-ID e o group-ID dum ficheiro. Mas se o ficheiro é referenciado por
um elo simbólico (symbolic link), a função lchown modifica o proprietário do elo simbólico, não o do
próprio ficheiro. Note-se que, em geral e à excepção do superutilizador, um
utilizador só poderá alterar a propriedade e o grupo dos seus próprios
ficheiros.
Elos
para ficheiros
Para perceber a utilidade de elos para ficheiros, há que compreender em
primeiro lugar a organização dum sistema de ficheiros.
Um disco tem uma ou mais partições e cada partição contem um sistema de
ficheiros (Figura 10.1).
Figura 10.1: Disco,
partições e um sistema de ficheiros.
Cada ficheiro tem um i-node e é identificado por um i-node number no
sistema de ficheiros. Um i-node é um registo de tamanho fixo que contêm a maior parte de informação
acerca dum ficheiro. Os i-nodes fornecem informações importantes sobre
ficheiros nomeadamente: propriedade individual (owner)
e de grupo (group), modo de acesso (permissões read, write e execute) e tipo de
ficheiro.
Figura 10.2: Sistema de
ficheiros com mais detalhe.
A Figura 10.2
mostra o seguinte:
Dois itens em
directorias podem apontar para o mesmo i-node. Cada i-node tem um contador de
elos (link count) que contém o número de itens em
directorias que apontam para ele. Quando este contador vai a zero, o respectvo ficheiro pode ser apagado, i.e. os blocos de
dados (data blocks) associados ao ficheiro podem ser
libertados. Isto explica porque a operação de “cortar o elo a ficheiro” não
significa necessariamente “apagar os blocos associados ao dito ficheiro”. Na
estrutura stat, o contador de elos está contido na
componente st_link, a qual é uma variável do tipo nlink_t. Estes
elos são os chamados elos fortes (hard links).
Há outro tipo de elos, designado por elo
simbólico (symbolic link). Neste
caso, o conteúdo do ficheiro (os blocos de dados) contém o nome do ficheiro
para o qual o elo aponta. No seguinte exemplo,
lrwxrwxrwx 1 root 7 Sep 25
o ficheiro lib é um elo simbólico (tipo de ficheiro l) que contém 7 bytes referentes ao nome usr/lib do ficheiro apontado.
Portanto, um elo simbólico não é mais do que um ponteiro indirecto para
um ficheiro. Por outro lado, um elo (forte) aponta directamente para o i-node
dum ficheiro.
O i-node contém toda a informação acerca dum ficheiro: tipo de ficheiro,
bits de permissão de acesso, tamanho do ficheiro, ponteiros para os blocos de
dados do ficheiro, etc. A maior parte da informação na estrutura stat é obtida a partir do i-node do ficheiro. Só dois itens
são armazenados numa directoria: o nome do ficheiro e o seu i-node number.
Funções de
manipulação de elos: link, unlink, remove e rename
Já vimos que qualquer
ficheiro pode ser apontado por vários verbetes (entries)
existentes em directorias. A forma de criar um elo para um ficheiro é através
da função link. Do mesmo modo, o corte dum elo
para um ficheiro é feito através da chamada da função unlink.
#include <unistd.h>
int
link(const char *existingpath, const char *newpath);
int
unlink(const char *pathname);
Ambas retornam: 0 se OK, -1 em
caso de erro
Exemplo 10.3
O seguinte programa abre o ficheiro teste.txt e, de seguida, corta o elo (forte) que ele tem na
directoria corrente. Isto é, o ficheiro teste.txt deixará de existir na directoria corrente. Antes de
terminar, o processo que executa o programa é colocado a dormir durante 15
segundos.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int
main(void)
{
if (open("teste.txt ", O_RDWR) < 0)
printf("open
error");
if (unlink("teste.txt ") < 0)
printf("unlink
error");
printf("file unlinked\n");
sleep(15);
printf("done\n");
exit(0);
}
A função unlink é muitas usada para assegurar que um eventual ficheiro temporário num
programa não seja deixado ao acaso numa situação de crash do programa. O
processo cria um ficheiro através da função open ou da função
creat, e depois chama de imediato a função unlink. No entanto,
o ficheiro não é logo eliminado porque ainda se encontra aberto. Só quando o
processo fecha (close) o ficheiro ou termina (o que faz com que o kernel
feche todos os seus ficheiros abertos) é que o ficheiro é eliminado.
Note-se que se o pathname é um elo
simbólico, a função unlink corta o elo ao ficheiro
que é o elo simbólico, não ao ficheiro apontado pelo elo simbólico.
Também se pode cortar
o elo (unlink) a um ficheiro ou directoria com a
função remove. No caso dum ficheiro, a função remove é idêntica à função
unlink. No caso duma directoria, a função remove
é idêntica à função rmdir.
#include <stdio.h>
int
remove(const char *pathname);
int rename(const char *oldname, const char *newname);
Ambas retornam: 0 se OK, -1 em
caso de erro
Funções de
manipulação de elos simbólicos: symlink e readlink
#include <unistd.h>
int
symlink(const char *actualpath, const char *sympath;
int readlink(const char *pathname, char *buf, int bufsize);
symlink retorna: 0 se OK, -1 em caso de
erro
readlink retorna: número de bytes se OK,
-1 em caso de erro
Um elo simbólico é
criado pela função symlink. Um novo item sympath é criado na
directoria corrente que passa a apontar para actualpath. Não é necessário
que actualpath exista quando o elo
simbólico é criado. Além disso, actualpath e sympath não precisam de
residir no mesmo sistema de ficheiros.
A função readlink serve para ler o conteúdo do elo simbólico, i.e.
ler o nome do ficheiro apontado pelo elo simbólico. Note-se que este conteúdo
não é null-terminated. Esta função combina as funções
open, read e close de manipulação de ficheiros.
Função de
temporização de ficheiros: utime
Há três itens temporais
associados a cada ficheiro: st_atime
(last-access time of file data), st_mtime
(last-modification time of file data) e st_ctime
(last-change time of i-node status). O primeiro fornece o
tempo do último acesso ao ficheiro, o segundo fornece o tempo da última
alteração ao conteúdo do ficheiro e, finalmente, o terceiro refere-se a
qualquer alteração do estado (permissões, user-ID, etc) do ficheiro, mas não do seu conteúdo.
O tempo de acesso e o
tempo de modificação dum ficheiro podem ser alterados através da função utime.
#include <sys/types.h>
#include <utime.h>
int
utime(const char *pathname, const struct
utimbuf *times);
Retorna: 0 se OK, -1 em caso de
erro
A estrutura usada por esta função é a seguinte:
struct utimbuf {
time_t actime;
/* access time */
time_t modtime; /* modification time */
}
Funções de
manipulação de directorias: mkdir e rmdir
Uma directoria
(vazia) pode ser criada através da invocação da função mkdir:
#include <sys/types.h>
#include <sys/stat.h>
int
mkdir(const char *pathname, mode_t mode);
Retorna: 0 se OK, -1 em caso de
erro
Os verbetes (entries) relativos às directorias .
e .. são automaticamente
criadas. As permissões de acesso ao ficheiro, ou seja mode,
são modificadas pela máscara de criação de modo de ficheiro do processo em
execução.
Um erro comum na
criação duma directoria é especificar um mode
idêntico ao da criação dum ficheiro (só permissões de leitura e escrita), mas
pelo menos um bit de execução deve ser activado para que haja acesso aos
ficheiros existentes numa directoria.
Para remover uma
directoria usa-se a seguinte função:
#include <unistd.h>
int
rmdir(const char *pathname);
Retorna: 0 se OK, -1 em caso de
erro
Funções de leitura de directorias
Uma directoria pode
ser lida por qualquer utilizador que tenha permissão para lê-la.
Mas, só o kernel pode escrever na directoria
por forma a preservar a sanidade do sistema de ficheiros.
#include
<sys/types.h>
#include <dirent.h>
DIR *opendir(const
char *pathname); Retorna: ponteiro se OK, NULL em caso de
erro
struct dirent *readdir(DIR *dp); Retorna: ponteiro se OK, NULL em caso de
erro
void
rewinddir(DIR *dp);
int closedir(DIR *dp);
Retorna: 0 se OK, -1 em caso de
erro
A estrutura dirent está definida no
ficheiro <dirent.h> e tem, entre outros, os seguintes
campos:
No entanto, esta
estrutura é dependente da implementação do sistema operativo.
A estrutura DIR é uma
estrutura interna que é usada pelas quatro funções anteriores. Esta estrutura
guarda a informação acerca da directoria que está a ser lida. Tem um propósito
semelhante à estrutura FILE para ficheiros.
O ponteiro para uma
estrutura DIR que é devolvido pela função opendir
é depois usado pelas outras três funções.
Exemplo 10.4:
Uma versão do comando
ls do
Unix.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char **argv)
{
DIR
*dp;
struct dirent
*dirp;
if (argc
!= 2)
fprintf(stderr, “Usage: %s Um argumento é necessário\n”, argv[0]);
if ((dp = opendir(argv[1])) == NULL)
fprintf(stderr,
“Can’t open %s\n”, argv[1]);
while ((dirp = readdir(dp)) != NULL)
printf(“%s\n”,
dirp->d_name);
closedir(dp);
}
Exemplo 10.5:
Outra versão do
comando ls
do Unix.
As funções a usar são
as seguintes:
§ Scans
o
conteúdo duma directoria:
scandir(char *dirname,struct
direct *namelist,int(*select)(),int(*compar)())
A função scandir() , onde os
argumentos são:
q dirname identifica a
directoria a ser lida;
q namelist é um ponteiro para
um array de ponteiros para estruturas (ou structs);
q (*select)() é um ponteiro para uma função que é
chamada com um ponteiro para uma directory entry (definida em <sys/types>) e que devolve um valor não-nulo se a directory entry está incluída no array. Se este ponteiro é NULL, então todas as directory entries serão incluídas.
q (*compar)() é um ponteiro para uma rotina que é
passada para o qsort (o quicksort
que ordena os elementos dum array). Se o ponteiro é
NULL, o array não é ordenado.
§ Ordena
alfabeticamente um array :
alphasort(struct direct **d1, **d2)
Exemplo 10.6:
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/param.h>
#include <stdio.h>
int file_select(struct dirent *entry); /*
tirar os ficheiros . e .. da listagem */
char pathname[MAXPATHLEN];
main()
{
int count, i;
struct dirent
**files;
if (getwd(pathname)==NULL)
{
printf(“Error
getting path\n”);
exit(0);
}
printf(“Current
working directory = %s\n”, pathname);
count=scandir(pathname,&files,file_select,alphasort);
/*
If no files found, make a non-selectable menu item */
if (count <= 0)
{
printf(“No
files in this directory\n”);
exit(0);
}
printf(“Number
of files = %d\n”,count);
for (i=1;i<count+1;++i)
printf(“%s”,files[i-1]->d_name);
putchar(“\n”);
}
int file_select(struct dirent *entry)
{
if ((strcmp(entry->d_name,“.”) == 0) || (strcmp(entry->d_name, “..”)==0))
return(FALSE);
return(TRUE);
}
A função scandir() também devolve ‘.’
(da directoria corrente) e ‘..’ (da directoria
imediata e hierarquicamente superior), mas são excluídas pela função file_select().
As funções scandir() e alphasort() têm os seus
protótipos em <sys/types.h> e <sys/dir.h>,
respectivamente.
As definições de MAXPATHLEN e getwd() encontram-se em
<sys/param.h>.
Exercício 10.3:
Altere o programa
anterior de modo a listar somente os ficheiros com extensão
.c, .o ou .h.
Exercício 10.4:
Escreva um programa sv com uma
funcionalidade semelhante ao comando cp do Unix. A sua sintaxe é a seguinte: sv <file1> <file2> … <filen> directory
O comando sv copia os ficheiros <file1> <file2> … <filen> para a directoria directory mas só se forem mais
recentes que as versões já existentes em directory.
/* sv.h
file */
#include <stdio.h> /* headers */
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifdef
UNIX /* Windows or
Unix */
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#else
#include <io.h>
#include <sys/stat.h>
#endif
void sair(char *s1, char *s2);
void sv(char *file, char *dir);
char *progname;
int main(int argc, char **argv)
{
int i;
struct stat stbuf;
char *dir=argv[argc-1];
progname=argv[0];
if (argc<3)
sair(“Utilizacao
file1 file2 … filen directory\n”,
“ ”);
if (stat(dir,&stbuf)==-1)
sair(“Nao consegue aceder a directoria %s\n”, dir);
if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
sair(“%s
nao e uma directoria\n”,dir);
for
(i=1;i<argv-1;i++)
sv(argv[i],dir);
return 0;
}
void sair(char *s1, char *s2) //Função para sair quando
surge um erro
{ //Esta
função devia ser melhorada e explicada – Um Voluntário?
extern int errno, sys_nerr;
extern char *progname;
#ifdef UNIX
extern char *sys_errlist[];
#else
extern char *_sys_errlist[];
#endif
fprintf(stderr, “%s”, progname);
fprintf(stderr,s1,s2);
if (errno>0 && errno<sys_nerr)
#ifdef UNIX
fprintf(stderr, “(%s)”, sys_errlist[errno]);
#else
fprintf(stderr, “(%s)”, _sys_errlist[errno]);
#endif
fprintf(stderr, “\n”);
exit(1);
}
void sv(char *file, char *dir)
{
struct stat stin,
stout;
int fin, fout,
n;
char target[BUFSIZ], buf[BUFSIZ];
sprintf(target,
“%s/%s”, dir, file);
if ((strchr(file, ‘/’))!=NULL)
sair(“Nao ha
tratamento do / no ficheiro %s”, file);
if
(stat(file,&stin)==-1) sair(“Nao consegue stat no ficheiro: %s”,
file);
if (stat(target,&stout)==-1) /* target nao existe */
stout.st_mtime=0;
if (stin.st_mtime <= stout.st_mtime)
fprintf(stderr,
“%s nao foi copiado\n”, file);
else{
fin = open(file,0);
fout = creat(target,stin.st_mode);
while ((n=read(fin,buf,sizeof(buf)))>0)
if (write(fout,buf,n)!=n)
sair(“Erro a escrever
%s”, target);
fprintf(stderr, “%s
copiado\n”, file);
close(fin);
close(fout);
}
}
Funções chdir,
fchdir e getcwd
Todo o processo tem
uma directoria corrente. Esta directoria é onde a pesquisa de qualquer caminho
(pathname) começa. Quando um utilizador entra no
sistema, a directoria corrente de trabalho começa normalmente por ser a
directoria especificada no sexto campo do verbete do utilizador no ficheiro /etc/passwd, também conhecida
por directoria base do utilizador.
A directoria corrente
é um atributo dum processo; a directoria de base é um atributo dum nome de
utilizador autorizado (login name).
A directoria corrente
pode ser alterada por um processo através das seguintes funções:
#include <unistd.h>
int
chdir(const char *pathname);
int fchdir(int filedes);
Ambas retornam: 0 se OK, -1 em
caso de erro
Note-se que a
directoria corrente é um atributo dum processo, o que significa que a
directoria corrente não pode ser afectada por qualquer sub-processo
que execute a função chdir.
Por exemplo, no
Exemplo 10.7, a mudança de directoria não provoca alteração de directoria no
processo progenitor que é a Bourne ou a C-shell. Isto é, quando o programa termina a execução, o
controlo volta à shell e a directoria corrente antes
da execução do programa do Exemplo 10.7.
Exemplo 10.7:
#include <stdio.h>
#include <unistd.h>
char cur[100];
main(int argc, char **argv)
{
if (argc < 2)
{
printf(“Usage: %s <pathname>\n”, argv[0]);
exit(1);
}
getwd(cur)
if (chdir(argv[1])
!= 0)
{
printf(“Error in chdir\n”);
exit(1);
}
printf(“Mudamos
da directoria %s\n”, cur);
printf(“para
a directoria %s\n”, getwd(cur));
}
A função que obtém a directoria corrente é a seguinte:
#include <unistd.h>
char *getcwd(char *buf, size_t size);
Retorna: buf
se OK, NULL em caso de erro
Exercício 10.5:
Escreva um programa
semelhante ao comando ls –l do Unix
que mostre todos os ficheiros duma directoria, incluindo as suas permissões.
(Nota: Não deve usar
a função exec.)
Exercício 10.6:
Escreva um programa
semelhante ao comando grep
do Unix que mostre todas as linhas dum ficheiro que contêm uma dada palavra.
Exercício 10.7:
Escreva um programa
que mostre todos os ficheiros duma directoria e das suas subdirectorias.
Exercício 10.8:
Escreva um programa
que iniba a permissão de leitura dum ficheiro ao grupo a que pertence o seu
proprietário.