ファイルの一覧を取得する

NSFileManagerの-enumeratorAtPath:を呼ぶと、特定ディレクトリの下を深さ優先でトラバースするNSDirectoryEnumeratorオブジェクトを返してくれるので、それで全ファイルエントリを取得出来るようだ。

但し、iTunesで買ったアプリなどは見えない*1。他にも見えないファイルがあるようだが、パーミッションの問題ではないようだ。別の方法で見せないようにしているっぽい。

/* CLANG_ENABLE_OBJC_ARC = YES */

#define TYPE_CHAR(fileType) (\
      fileType == NSFileTypeDirectory         ? 'd' \
    : fileType == NSFileTypeSymbolicLink      ? 'l' \
    : fileType == NSFileTypeSocket            ? 's' \
    : fileType == NSFileTypeCharacterSpecial  ? 'c' \
    : fileType == NSFileTypeBlockSpecial      ? 'b' \
    : fileType == NSFileTypeUnknown           ? '?' \
    :                                           '-' \
)

#define WHO_OWNER   2
#define WHO_GROUP   1
#define WHO_OTHER   0

/* non-zero if the file is readable */
#define READABLE(mode, who)   (mode >> (who * 3 + 2) & 1)
/* non-zero if the file is writable */
#define WRITABLE(mode, who)   (mode >> (who * 3 + 1) & 1)
/* non-zero if the file is executable or the directory is searchable */
#define EXECUTABLE(mode, who) (mode >> (who * 3 + 0) & 1)
/* non-zero if set-x-ID or sticky bit is set */
#define SBIT_SET(mode, who)   (mode >> (who + 9) & 1)

#define SBIT_CHAR(mode, who) (\
    who == WHO_OTHER \
        ? (EXECUTABLE(mode, who) ? 't' : 'T') \
        : (EXECUTABLE(mode, who) ? 's' : 'S') \
)\

#define MODE_CHARS(mode, who) \
    READABLE(mode, who) ? 'r' :'-', \
    WRITABLE(mode, who) ? 'w' :'-', \
    SBIT_SET(mode, who) \
       ? SBIT_CHAR(mode, who) \
       : EXECUTABLE(mode, who) ? 'x' :'-'

void
show_files(NSString *directoryPath)
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"MM dd HH:mm:ss yyyy"];

    NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:directoryPath];
    NSString *filePath;
    while (filePath = [enumerator nextObject]) {
        NSString *fullPath = [directoryPath stringByAppendingPathComponent:filePath];

        NSDictionary *attrs = [enumerator fileAttributes];
        NSString *fileType = [attrs objectForKey:NSFileType];
        short mode = [[attrs objectForKey:NSFilePosixPermissions] shortValue];
        NSString *owner = [attrs objectForKey:NSFileOwnerAccountName];
        NSString *group = [attrs objectForKey:NSFileGroupOwnerAccountName];
        NSDate *modificationDate = [attrs objectForKey:NSFileModificationDate];
        
        unsigned long long fileSize = [[attrs objectForKey:NSFileSize] unsignedLongLongValue];

        printf("%c" "%c%c%c" "%c%c%c" "%c%c%c" " %-8s %-8s %10qu %s %s\n",
                TYPE_CHAR(fileType),
                MODE_CHARS(mode, WHO_OWNER), MODE_CHARS(mode, WHO_GROUP), MODE_CHARS(mode, WHO_OTHER),
                [owner UTF8String], [group UTF8String], fileSize,
                [[dateFormat stringFromDate:modificationDate] UTF8String],
                [fullPath fileSystemRepresentation]);
    }
}

show_files(@"/Developer/usr/bin/")してみた。

-rwxrwxr-x root     admin         14496 02 02 16:20:17 2012 /Developer/usr/bin/chudRemoteCtrl
-rwxrwxr-x root     admin        152032 08 10 14:05:50 2011 /Developer/usr/bin/debugserver
-rwxrwxr-x root     admin         20176 02 02 16:20:33 2012 /Developer/usr/bin/hwprefs
-rwxrwxr-x root     admin        106016 01 16 12:09:06 2012 /Developer/usr/bin/reg
-rwxrwxr-x root     admin         18064 02 05 07:10:09 2012 /Developer/usr/bin/ScreenShotr
-rwxrwxr-x root     admin        117360 02 02 16:20:29 2012 /Developer/usr/bin/shark

他のディレクトリも含め、setuidしてるファイルは見つからなかった。
ソケットやシンボリックリンクはそれなりに見つかった。


拡張属性を調べるには、xattr.hにある関数を使えば良いっぽい。

#include <sys/types.h>
#include <sys/xattr.h>
...
        ssize_t bufsize = listxattr([fullPath fileSystemRepresentation], NULL, 0,0);
        if (bufsize > 0){
            char *buf = malloc(bufsize);
            listxattr([fullPath fileSystemRepresentation], buf, bufsize,0);
            char *ptr = buf;
            while(ptr <  buf + bufsize){
                ssize_t attrsize = getxattr([fullPath fileSystemRepresentation], ptr, NULL, 0, 0, 0);
                printf("\t%s\t%ld\n", ptr, attrsize);
                ptr += strlen(ptr) + 1;
            }
            free(buf);
        }
...

拡張属性が付いているファイルは一つしか見つからなかった。

-rw-r--r-- mobile   mobile         5662 01 18 13:58:01 2012 /private/var/mobile/Media/PhotoData/MISC/PreviewWellImage.tiff
	com.apple.assetsd.thumbnailCameraPreviewImageAssetID	58


ファイルの中身は普通にFoundationのクラスでアクセス出来る。

        NSData *data = [NSData dataWithContentsOfFile:@"/Developer/usr/bin/debugserver"];
        NSLog(@"data = %@", data);

適当に一つiPad2からLion上に持って来てotoolで中身をみたら、ARM_V7用バイナリだった。

daphne:tmp terazzo$ otool -h debugserver
debugserver:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00          2    19       2000 0x00210085

/usr/include/mach/machine.h によると

#define CPU_TYPE_ARM            ((cpu_type_t) 12)
#define CPU_SUBTYPE_ARM_V7              ((cpu_subtype_t) 9)

追記: パーミッションのとこのAssert

ちょっと書いてた。関数の中から呼ぶときはNSAssertじゃなくてNSCAssertを使う。

#define ASSERT_MODE(mode, who, str) \
    NSCAssert2(([[NSString stringWithFormat:@"%c%c%c", MODE_CHARS(mode, who)] isEqualToString:str]), \
         @"'%@' expected but was '%@'", str, ([NSString stringWithFormat:@"%c%c%c", MODE_CHARS(mode, who)]))

void
assert_mode()
{
    ASSERT_MODE(00000, WHO_OWNER, @"---");
    ASSERT_MODE(00100, WHO_OWNER, @"--x");
    ASSERT_MODE(00200, WHO_OWNER, @"-w-");
    ASSERT_MODE(00300, WHO_OWNER, @"-wx");
    ASSERT_MODE(00400, WHO_OWNER, @"r--");
    ASSERT_MODE(00500, WHO_OWNER, @"r-x");
    ASSERT_MODE(00600, WHO_OWNER, @"rw-");
    ASSERT_MODE(00700, WHO_OWNER, @"rwx");

    ASSERT_MODE(04000, WHO_OWNER, @"--S");
    ASSERT_MODE(04100, WHO_OWNER, @"--s");
    ASSERT_MODE(04200, WHO_OWNER, @"-wS");
    ASSERT_MODE(04300, WHO_OWNER, @"-ws");
    ASSERT_MODE(04400, WHO_OWNER, @"r-S");
    ASSERT_MODE(04500, WHO_OWNER, @"r-s");
    ASSERT_MODE(04600, WHO_OWNER, @"rwS");
    ASSERT_MODE(04700, WHO_OWNER, @"rws");
    
    ASSERT_MODE(01000, WHO_OTHER, @"--T");
    ASSERT_MODE(01001, WHO_OTHER, @"--t");
    ASSERT_MODE(01002, WHO_OTHER, @"-wT");
    ASSERT_MODE(01003, WHO_OTHER, @"-wt");
    ASSERT_MODE(01004, WHO_OTHER, @"r-T");
    ASSERT_MODE(01005, WHO_OTHER, @"r-t");
    ASSERT_MODE(01006, WHO_OTHER, @"rwT");
    ASSERT_MODE(01007, WHO_OTHER, @"rwt");
}

追記2: 見えないディレクト

readdirで存在は確認できるがstatするとEPERMが戻ってくる。サンドボックス外のディレクトリについてはそういう風になるようだ。

#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
...
        const char base_dir[] = "/var/mobile/Applications/";
        DIR *dir = opendir(base_dir);
        if (dir) {
            char path[MAXPATHLEN + 1];
            struct dirent *dirent;
            struct stat st;
            while ((dirent = readdir(dir)) != NULL) {
                if (strlen(base_dir) + strlen(dirent->d_name) > MAXPATHLEN) continue;
                if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) continue; 
                strcpy(path, base_dir);
                strcat(path, dirent->d_name);
                printf("file: \t%s\n", dirent->d_name);
                if (stat(path, &st) == -1) {
                    perror("\t*** stat failed");
                    continue;
                }
                
                printf("\tmode:%0.6o\n", st.st_mode);
            }
            closedir(dir);
        }
file: 	4335AC47-9753-4D5E-BF9F-52507E510B8C
	*** stat failed: Operation not permitted
file: 	AD45441A-D37B-4B6A-8A46-3DEC149E0C54
	*** stat failed: Operation not permitted
file: 	BCAB6052-D9F7-4F52-9496-5F102EDC93B0
	mode:040755

一番下の奴はこのプログラム自体を動かしているアプリケーションなので見えている。

*1:前エントリの「/var/mobile/Applications/4335AC47-9753-4D5E-BF9F-52507E510B8C/iBooks.app」などは見えなかった