[ ZIP extended data descriptor and signature issue at Python zipfile library. (Python 2.7.12) ]

Zip format spec을 보면, extended data descriptor쪽이 불명확하게 되어 있다

원칙적이로는 다음과 같은 format을 따른다.(참고: http://www.pkware.com/documents/casestudies/APPNOTE.TXT)

 4.3.9  Data descriptor:

        crc-32                          4 bytes
        compressed size                 4 bytes
        uncompressed size               4 bytes

문제는, De-facto standard로 아래와 같이 정의되는 경우가 대부분이라는 것이다. (http://www.onicos.com/staff/iz/formats/zip.html)

Extended local header:
Offset   Length   Contents
  0      4 bytes  Extended Local file header signature (0x08074b50)
  4      4 bytes  CRC-32
  8      4 bytes  Compressed size
 12      4 bytes  Uncompressed size

[ 7z - 16.02 버젼 ]

CPP/7zip/Archive/Zip/ZipIn.cpp: CInArchive::ReadLocalItemAfterCdItemFull()

    if (item.HasDescriptor())   // <= 0x8 bit of general purpose flag, is set
      // pkzip's version without descriptor is not supported
      RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize));
      if (ReadUInt32() != NSignature::kDataDescriptor)
        return S_FALSE;

와 같이 De-facto standard를 따르고 있다.

[ Python zipfile library ]

zipfile.ZipFile 의 write 혹은 writestr 함수를 보면

< writestr 함수 >
        if zinfo.flag_bits & 0x08:
            # Write CRC and file sizes after the file data
            fmt = '<LQQ' if zip64 else '<LLL'
            self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size,

와 같이 signature가 빠져 있다. 즉 standard를 따른다!

[ 누구의 잘못인가? ]

AppNote(http://www.pkware.com/documents/casestudies/APPNOTE.TXT)는 아래와 같이 말하고 있다.

      Although not originally assigned a signature, the value
      0x08074b50 has commonly been adopted as a signature value
      for the data descriptor record.  Implementers should be
      aware that ZIP files may be encountered with or without this
      signature marking data descriptors and should account for
      either case when reading ZIP files to ensure compatibility.
      When writing ZIP files, it is recommended to include the
      signature value marking the data descriptor record.  When
      the signature is used, the fields currently defined for
      the data descriptor record will immediately follow the
즉, 원칙적으로는 7z 이 양쪽의 경우를 다 지원하도록 확장되는게 맞는것 같다.
다만 python의 zipfile library역시 recommendation을 따르는게 좋을 것 같으나... 그럴 기미가 안보인다. (Python3.5.2 에서도 zipfile library는 여전히 순수 표준을 따른다.)

[ 언제 문제가 되는가? ]

extended data descriptor를 사용하는 걸로 set된 zip file을 python library로 update할 경우, 혹은 python library로 extended data descriptor를 사용해서 zip을 생성할 경우, 이렇게 생성된 zip file은 다른 popular한 tool에서 사용할 수 없을 수도 있다.
    7z 의 경우, 'x'(extract)는 잘 되나, 'u'(update)에서는 'E_NOTIMPL' System ERROR가 발생한다.
    'zip'(pkzip)의 경우 양쪽 모두 잘 지원한다. 다만... 다른 bug가...(>4G => <4G => >4G 버그?)

또한 7z의 경우, 내부적으로 병렬로 pkzip을 수행하는 것으로 보인다.

$ time 7z ...

의 command로 확인해보면, 바로 알 수 있다. 또한 속도도 빠르다!

블로그 이미지


Environment : Ubuntu 14.04
Tools : sem, flock(recommend)

> $ cat a.sh

for i in {1..7}; do

for i in {1..10}; do
    echo $str >> out                   # (*a)
#    sem "echo $str >> out"            # (*b)
#    flock out -c "echo $str >> out"   # (*c)

> $ cat runa
for i in {1..10}; do

> $ cat runa2

for i in {1..10}; do

> $ ./runa2

$ cat parout | uniq | wc -l
if 1 => OK, otherwise not-syncronized!!

(*a) ==> race condition!! :
(*c) sem ==> error!(sem bug!!)
(*b) flock ==> OK

블로그 이미지


[ related information ]
* prctl
* See linux kerne for details ("kernel/exit.c : find_new_reaper())

------ commit(kernel/git/torvalds/linux.git) -----

author	  Lennart Poettering <lennart@poettering.net>    2012-03-23 22:01:54 (GMT)
committer Linus Torvalds <torvalds@linux-foundation.org> 2012-03-23 23:58:32 (GMT)
commit	  ebec18a6d3aa1e7d84aab16225e87fd25170ec2b (patch)

[ test code ]

$ cat b.sh
function echosleep2 {
    sleep 10
    echo 'sleep2 end'

function echosleep {
    sleep 5
    echo 'sleep end'

echo "hello: $BASHPID $$"
sleep 2
echo end


 3241  2573  3241  3241 init --user
 4392  3241  3374  3374 gnome-terminal
 9882  4392  9882  9882 bash

[ < 2 seconds ]

33513  9882 33513  9882 bash            # ./b.sh (*a)
33514 33513 33513  9882 bash            # ./b.sh:echosleep() (*b)
33515 33513 33513  9882 sleep 2
33516 33514 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33517 33514 33513  9882 sleep 5
33518 33516 33513  9882 sleep 10
33519 15742 33519 15742 ps -e -o pid,ppid,pgid,sid,cmd

(*a): Bash terminal executed 'b.sh' as new process group leader.
      All child/grandchild processes are executed in the same process group.

[ < 5 seconds ]

33514  3241 33513  9882 bash            # ./b.sh:echosleep() (*b)
33516 33514 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33517 33514 33513  9882 sleep 5
33518 33516 33513  9882 sleep 10
33520 15742 33520 15742 ps -e -o pid,ppid,pgid,sid,cmd

(*b): Process group leader(*a) is disappeared.
      And there is no threads in this process.
      (If there is other threads in this process, it will become new reaper)
      So, it is re-parented to orphan-reaper("init --user").

[ < 10 seconds ]

33516  3241 33513  9882 bash            # ./b.sh:echosleep():echosleep2() (*c)
33518 33516 33513  9882 sleep 10
33522 15742 33522 15742 ps -e -o pid,ppid,pgid,sid,cmd

(*c): Same with above(*b).

[ > 10 seconds ]

33524 15742 33524 15742 ps -e -o pid,ppid,pgid,sid,cmd


Sample code to get every SIGCHLD from descendents.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <errno.h>

void handle_signal(int signal) {
        pid_t pid;
        printf("Handler SIGCHLD: %d\n", signal);
        if (0 > (pid = wait(NULL))) {
		printf("Wait fails: errno: %s", strerror(errno));
	} else {
		printf("Wait done: %d\n", pid);

int main() {
        struct sigaction sa;
	pid_t pid;

        // Print pid, so that we can send signals from other shells
        printf("My pid is: %d\n", getpid());

        // Setup the sighub handler
        sa.sa_handler = &handle_signal;

        // Intercept SIGHUP and SIGINT
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
                perror("Error: cannot handle SIGCHLD"); // Should not happen

        if (0 > prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0)) {
                perror("Error: prctl");

	pid = fork();
	if (pid) {
		int secs = 10;
		// parent
		printf("\nSleeping for 10 second\n");
		while (secs > 0)
			secs = sleep(10);
		printf("Parent DONE\n");
	} else {
		// child
		if (0 > execlp("bash", "bash", "b.sh", NULL)) {
			printf("execl fails: %s\n", strerror(errno));

블로그 이미지


Linux Kernel 3.10을 기준으로 보면,

Maximum number of environment variable in Linux : 0x7fffffff

Maximum string length of environment variable and argument: PAGE_SIZE * 32 (4K page의 경우 128K)

헛갈리기 쉬운 것 (용어를 명확하게 사용하고 있지 않은 것들.)

"environment variable" vs "shell variable"


Bash 기준으로 보면


export var=hello  # This is environment variable

var=hello   # This is shell variable.

즉, environment variable 이란 descendent process들에게 영향을 미치는 '환경'을 의미한다. Current process에서만 유효한 것은 environment variable이라 부를 수 없다.

추가적으로, '$ env' command를 수행할 때 나오는 값들 역시 environment variable만 나오고 shell variable은 나오지 않는다.

블로그 이미지


ext4에서 'readdir' system call을 이용해서 directory를 읽으면, directory가 가지는 file list를 읽을 수 있다.

이때, 읽히는 file의 순서는 어떻게 정해지는가?

경험적으로 대부분의 개발자라면, 이 순서가 일정하지 않다는 것을 알고 있을 것이다.

이 부분에 대해서 좀더 깊게 살펴보기도 하자.

linux kernel에서 ext4 관련 부분의 코드를 분석해 보면 아래의 사실들을 알아 낼 수 있다.

- ext4는 directory내에서 file search등을 빠르게 하기 위해서 htree(hash tree)를 이용한다.

- 이때 사용되는 hash값의 order가 곧 readdir 로 읽어들이는 file의 order이다.

- hash 값은 'HashFunc(<hash algorithm>, <hash seed>, <file name>)"을 통해서 구한다.

- hash algorithm과 hash seed의 경우, super block에 적힌 값을 사용하면, 없는 경우 default 값을 사용한다.

- directory내의 각 file의 hash값은 directory file의 'f_pos'값 (file position값)으로 사용된다.

즉, super block에 적힌 hash algorithm과 hash seed에 따라서, directory가 같은 이름들의 file을 가지고 있다고 하더라도, readdir이 읽어 오는 file의 순서가 달라진다는 말이다.

실제로 이를 확인하기 위해서는 'dumpe2fs' 와 'debugfs' tool을 사용할 수 있다.

Assumption : '/' 가 '/dev/sda1' 에 mount 되어 있음.

>$ sudo dumpe2fs -h /dev/sda1
Default directory hash:   half_md4     <== hash algorithm
Directory Hash Seed:      5841608b-14fe-405e-8d28-76236cc8c496  <== seed (UUID format)

이후 아래와 같은 방법으로 각 file name에 해당하는 hash 값을 알 수 있다.

>$ sudo debugfs /dev/sda1
dx_hash -h half_md4 -s c773a461-6150-4fe8-abe8-96acc6086d7e vmlinuz
Hash of /vmlinuz is 0xf097024e (minor 0x0)
추가적으로 'ls -f' 를 사용하면, sorting되지 않은 순서로 file list를 읽을 수 있는데, 이 순서가 바로, hash 값에 따른 순서 즉 readdir로 읽어 들이는 순서이다.
이를 확인하기 위해서는
>$ rm dxhashs; for e in $(ls -f /); do echo "dx_hash -h half_md4 -s 5841608b-14fe-405e-8d28-76236cc8c496 $e" >> dxhashs; done; sudo debugfs -f dxhashs /dev/sda1 | grep "Hash of" | cut -d' ' -f5

와 같이 하면, 정확하게 ascending order로 출력되는 hash 값을 볼 수 있다.
즉 'ls -f' 를 통해서 보여지는 file name의 순서가 hash값이 ascending order라는 뜻이고, hash 값이 ascending order에 따라 읽어 들이는 file name의 순서가 'readdir'로 읽어 들이는 순서와 일치한다.

블로그 이미지


일반 적으로...

exit code = 128 + (signal number)


exit code가 139(128  + 11)라면, 11번 signal이 뭔지 알아야 하고 "kill -l"로 확인할 수 있다.

보통의 경우 11번은 SIGSEGV  이다. 즉, segmentation fault로 죽었다는 말...

블로그 이미지




if [[ x$1 = x ]]; then

while read line
    echo $line
done < $f

블로그 이미지


Some notable points when using mmap 

- mapping with MAP_PRIVATE doesn't carry updates through to the underlying file.

- mmap writing to file is deferred.

=> use msync to write back to file at certain time.

- See also, mprotect, madvise ...

mmap and memory(smaps).

protection argument and flag of mmap matches vma flags (ex. MAP_PRIVATE + PROT_READ | PROT_EXEC <=> r-xp )

* mmap to generic file
Writing to mapped memory doesn't increase process's memory usage(RSS) - doesn't request process's memory page.(I think just memory for disk cache is affected by this operation.)

* mmap to anonymous - MAP_PRIVATE
Demanded pages are treated as PrivateDirty (just like malloc)

map = mmap(NULL, mapsz, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

there is only one VMA(size = mapsz) - Virtual Memory Area - for this map. And it's flag will be ---p.

mprotect(map, mapsz / 3, PROT_WRITE);

Now, two VMAs are used for this map.
One(vma0) is VMA(-w-p) whose size is mapsz / 3. The other(vma1) is VMA(---p) whose size is mapsz / 3 * 2.

sz = mapsz / 3; while (sz--) map[sz] = 0xff;

Now, vma0 has PrivateDirty (size = mapsz / 3) because pages are demanded and written.

* mmap to anonymous - MAP_SHARED   
Same with above. But, shared flag is used instead of private flag.
And in case of shared memory, PSS is very valuable information along with RSS.

(to be continued...)

블로그 이미지


ARM의 FSR(Fault Status Register) Spec을 보면

ARM9 까지는 Read/Write 상태를 알 수 없고 , ARM11부터 지원하는 것 처럼 보인다 - (1 << 11 bit - Linux kernel "fault.h")

그렇다면, ARM9에서 CoW는 어떤식으로 지원되었을까?

자세히 살펴보지는 않았지만, vendor에서 지원했거나, 아니면, MMU에서 해당 정보를 알 수 있는 방법을 제공했을 수도 있겠다.

(아니면... 내가 모르는 무언가가 있을수도...)

어쨌든 CoW는 memory write순간을 Processor가 알 수 있어야만 구현이 가능하다!

'Domain > Linux' 카테고리의 다른 글

Read line from file or standard input....  (0) 2013.11.28
[Linux] mmap and memory...  (0) 2013.10.07
CoW(Copy on Write) on ARM  (0) 2013.09.26
git server daemon 설치하기  (0) 2013.06.13
atime 대신에 strictatime을 사용하자!  (0) 2013.05.31
[Shell] Variable Name Expansion  (0) 2013.03.28
블로그 이미지


Ubuntu 12.04 64 bit LTS

* git 을 설치

* server가 실행될 때 항상 실행되도록 Upstart Script 등록

/etc/init/<script name>.conf


xxxx.conf : upstart script

xxxx.override : 기존의 xxxx.conf를 override함.

* /etc/init/git-daemon.conf 작성 (git project directory = /work/.prjrepo/)

start on runlevel [2345]                                                                        

stop on runlevel [!2345]                                                                        

expect fork                                                                                     


exec /usr/bin/git daemon --user=gitro --group=gitro --reuseaddr --base-path=/work/.prjrepo/ /work/.prjrepo/

* export하고자 하는 git repository안에 magic file인 git-daemon-export-ok 파일 생성 (size 0인 dummy file - 그냥 touch로 생성하면 된다.)

* client에서 test.

git clone git://<host>/<project>

<9418 포트 사용>

'Domain > Linux' 카테고리의 다른 글

[Linux] mmap and memory...  (0) 2013.10.07
CoW(Copy on Write) on ARM  (0) 2013.09.26
git server daemon 설치하기  (0) 2013.06.13
atime 대신에 strictatime을 사용하자!  (0) 2013.05.31
[Shell] Variable Name Expansion  (0) 2013.03.28
[Linux][Shell] Cleaning PATH environment variable.  (0) 2011.09.15
블로그 이미지