O atributo de arquivo externo do formato zip

25

Essa é uma pergunta um pouco exótica, mas não parece haver muita informação na rede sobre isso. Acabei de adicionar uma resposta a uma pergunta sobre o atributo de arquivo externo do formato zip . Como você pode ver na minha resposta, concluo que apenas o segundo byte (de 4 bytes) é realmente usado para Unix. Aparentemente, isso contém informações suficientes ao descompactar para deduzir se o objeto é um arquivo ou um diretório e também possui espaço para outras informações de permissão e atributo. Minha pergunta é: como isso é mapeado para as permissões usuais do Unix? Faça as permissões usuais do Unix (por exemplo, abaixo) que se lsencaixam em exatamente um byte e, nesse caso, alguém pode descrever o layout ou fornecer uma referência, por favor?

$ ls -la
total 36
drwxr-xr-x   3 faheem faheem  4096 Jun 10 01:11 .
drwxrwxrwt 136 root   root   28672 Jun 10 01:07 ..
-rw-r--r--   1 faheem faheem     0 Jun 10 01:07 a
drwxr-xr-x   2 faheem faheem  4096 Jun 10 01:07 b
lrwxrwxrwx   1 faheem faheem     1 Jun 10 01:11 c -> b

Deixe-me tornar isso mais concreto, fazendo uma pergunta específica. De acordo com o patch Trac citado na minha resposta acima, você pode criar um arquivo zip com o snippet de Python abaixo.

O 040755 << 16Lvalor corresponde à criação de um diretório vazio com as permissões drwxr-xr-x. (Eu testei). Reconheço que 0755corresponde ao rwxr-xr-xpadrão, mas e quanto a 04, e como o valor inteiro corresponde a um byte? Eu também reconheço que << 16Lcorresponde a um deslocamento à esquerda bit a bit de 16 lugares, o que faria com que fosse o segundo do byte superior.

def makezip1():
    import zipfile
    z = zipfile.ZipFile("foo.zip", mode = 'w')
    zfi = zipfile.ZipInfo("foo/empty/")
    zfi.external_attr = 040755 << 16L # permissions drwxr-xr-x
    z.writestr(zfi, "")
    print z.namelist()
    z.close()

EDIT: Ao reler isso, acho que minha conclusão de que as permissões do Unix correspondem apenas a um byte pode estar incorreta, mas deixarei o exposto acima no momento, pois não tenho certeza de qual é a resposta correta.

EDIT2: Eu estava realmente incorreto sobre os valores Unix correspondentes apenas a 1 byte. Como o @ Random832 explicou, ele usa os dois primeiros bytes. Pela resposta de @ Random832, podemos construir o 040755valor desejado a partir das tabelas que ele fornece abaixo. Nomeadamente:

__S_IFDIR + S_IRUSR + S_IWUSR + S_IXUSR + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH
0040000   + 0400    + 0200    + 0100    + 0040    + 0010    + 0004    + 0001
= 40755 

A adição aqui está na base 8 .

Faheem Mitha
fonte
Não sei nada sobre permissões zip, mas sei que as permissões unix tradicionais usam 12 bits, que são mais de um bytes. Talvez o zip não se incomode com setxid e pegajoso, mas isso ainda deixa 9 (rwx × ugo).
Gilles 'SO- stop be evil'

Respostas:

30

0040000é o valor tradicional de S_IFDIR, o sinalizador de tipo de arquivo que representa um diretório. O tipo usa os 4 bits superiores do valor de 16 bits st_mode , 0100000é o valor para arquivos regulares.

Os 16 bits altos dos atributos de arquivo externo parecem ser usados ​​para permissões específicas do SO. Os valores do Unix são os mesmos das implementações tradicionais do unix. Outros sistemas operacionais usam outros valores. Informações sobre os formatos usados ​​em vários sistemas operacionais diferentes podem ser encontradas no código-fonte Info-ZIP ( download ou, por exemplo, no debian apt-get source [zip or unzip]) - arquivos relevantes estão zipinfo.cdentro unzipe arquivos específicos da plataforma zip.

Estes são convencionalmente definidos em octal (base 8); isso é representado em C e python , prefixando o número com a 0.

Esses valores podem ser encontrados no <sys/stat.h>- link para a versão 4.4BSD . Eles não estão no padrão POSIX (que define macros de teste); mas são originários da AT&T Unix e BSD. (no GNU libc / Linux, os próprios valores são definidos como __S_IFDIRetc bits/stat.h, embora o cabeçalho do kernel possa ser mais fácil de ler - os valores são todos os mesmos em praticamente todos os lugares.)

#define S_IFIFO  0010000  /* named pipe (fifo) */
#define S_IFCHR  0020000  /* character special */
#define S_IFDIR  0040000  /* directory */
#define S_IFBLK  0060000  /* block special */
#define S_IFREG  0100000  /* regular */
#define S_IFLNK  0120000  /* symbolic link */
#define S_IFSOCK 0140000  /* socket */

E, é claro, os outros 12 bits são para as permissões e os bits setuid / setgid / sticky, o mesmo que para o chmod:

#define S_ISUID 0004000 /* set user id on execution */
#define S_ISGID 0002000 /* set group id on execution */
#define S_ISTXT 0001000 /* sticky bit */
#define S_IRWXU 0000700 /* RWX mask for owner */
#define S_IRUSR 0000400 /* R for owner */
#define S_IWUSR 0000200 /* W for owner */
#define S_IXUSR 0000100 /* X for owner */
#define S_IRWXG 0000070 /* RWX mask for group */
#define S_IRGRP 0000040 /* R for group */
#define S_IWGRP 0000020 /* W for group */
#define S_IXGRP 0000010 /* X for group */
#define S_IRWXO 0000007 /* RWX mask for other */
#define S_IROTH 0000004 /* R for other */
#define S_IWOTH 0000002 /* W for other */
#define S_IXOTH 0000001 /* X for other */
#define S_ISVTX 0001000 /* save swapped text even after use */

Como uma nota histórica, o motivo 0100000é que os arquivos regulares são substituídos por 0, pois nas versões mais antigas do unix, 0 era para arquivos 'pequenos' (eles não usavam blocos indiretos no sistema de arquivos) e o alto nível do sinalizador de modo era definido para arquivos 'grandes' que usariam blocos indiretos. Os outros dois tipos que usam esse bit foram adicionados em sistemas operacionais derivados do unix posteriores, após a alteração do sistema de arquivos.

Portanto, para finalizar, o layout geral do campo de atributos estendidos do Unix é

TTTTsstrwxrwxrwx0000000000ADVSHR
^^^^____________________________ file type as explained above
    ^^^_________________________ setuid, setgid, sticky
       ^^^^^^^^^________________ permissions
                ^^^^^^^^________ This is the "lower-middle byte" your post mentions
                        ^^^^^^^^ DOS attribute bits
Random832
fonte
@ Random832: Uau, isso é impressionante e completo. Você também pode explicar como o valor 040755 << 16Lé construído? Especificamente, que representação / base está usando (acho que possivelmente Octal ) e, o mais importante, como a linguagem (o intérprete Python nesse caso) sabe qual é a representação? Hmm, talvez o tipo seja declarado no código C. Além disso, de qual arquivo você está obtendo os valores "tipo de arquivo"? Adicionar alguns links / referências seria útil.
FaHemem Mitha
@ Random832: Vejo que zipinfo.cestá na fonte para descompactar no Debian . Alternativamente, pode-se usar o mais conveniente apt-get source unzip. Você pode anexá-lo à sua resposta ou usar uma fonte unstream. Eu costumo citar Debian, porque eu tenho fé que será em torno de um longo tempo :-).
Faheem Mitha
@ Random832: Ok, acho que vejo como isso funciona. Você apenas adiciona todos os valores para as coisas definidas na base 8, conforme sua tabela, e obtém o número 040755. Vale a pena mencionar o imo para pessoas que não sabem ou se esqueceram. Claro, isso ainda deixa a questão de como ele sabe que é base 8, mas talvez o tipo é declarado como base de 8.
Faheem Mitha
É base 8 porque começa com um 0. Eu vou esclarecer que em uma edição
Random832
@ Random: Obrigado pelo esclarecimento. Eu não estava ciente da principal convenção 0. O stat.harquivo no Linux (suponho que seja o arquivo correto /usr/include/sys/stat.h) não contém a definição dessas constantes de maneira tão clara quanto o arquivo ao qual você vinculou. Eles estão escondidos em outro lugar? Vejo que você usou o termo test macros, mas não tenho certeza do que isso significa.
Faheem Mitha