/* Cydia - iPhone UIKit Front-End for Debian APT
* Copyright (C) 2008-2014 Jay Freeman (saurik)
*/
/* GNU General Public License, Version 3 {{{ */
/*
* Cydia is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Cydia is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cydia. If not, see .
**/
/* }}} */
#include
#include
#include
#include
#include
#include
#include
#define _syscall(expr) ({ typeof(expr) _value; for (;;) { \
_value = (expr); \
if ((long) _value != -1) \
break; \
if (errno != EINTR) { \
perror(#expr); \
return -1; \
} \
} _value; })
int $getdirentries(int, char *, int, long *) __asm("_getdirentries");
struct $dirent {
__uint32_t d_ino;
__uint16_t d_reclen;
__uint8_t d_type;
__uint8_t d_namlen;
char d_name[__DARWIN_MAXNAMLEN + 1];
};
#define getdirentries $getdirentries
#define dirent $dirent
enum Recurse {
RecurseYes,
RecurseNo,
RecurseMaybe,
};
struct File {
int fd_;
File(int fd);
~File();
operator int() const;
};
File::File(int fd) :
fd_(fd)
{
}
File::~File() {
close(fd_);
}
File::operator int() const {
return fd_;
}
static int setnsfpn(const char *path, size_t before, Recurse recurse) {
File fd(_syscall(open_dprotected_np(path, O_RDONLY | O_SYMLINK, 0, O_DP_GETRAWENCRYPTED)));
if (recurse == RecurseMaybe) {
struct stat stat;
_syscall(fstat(fd, &stat));
switch (stat.st_mode & S_IFMT) {
case S_IFLNK:
return 0;
default:
return -1;
case S_IFDIR:
recurse = RecurseYes;
break;
case S_IFREG:
recurse = RecurseNo;
break;
}
}
int mode(_syscall(fcntl(fd, F_GETPROTECTIONCLASS)));
if (mode != -1 && mode != 4) {
if (recurse == RecurseYes) {
long address(0);
for (;;) {
char buffer[4096];
int size(_syscall(getdirentries(fd, buffer, sizeof(buffer), &address)));
if (size == 0)
break;
const char *next(buffer), *stop(next + size);
while (next != stop) {
const dirent *dir(reinterpret_cast(next));
const char *name(dir->d_name);
size_t after(strlen(name));
if (false);
else if (after == 1 && name[0] == '.');
else if (after == 2 && name[0] == '.' && name[1] == '.');
else {
size_t both(before + 1 + after);
char sub[both + 1];
memcpy(sub, path, before);
sub[before] = '/';
memcpy(sub + before + 1, name, after);
sub[both] = '\0';
switch (dir->d_type) {
case DT_LNK:
break;
default:
return -1;
case DT_DIR:
setnsfpn(sub, both, RecurseYes);
break;
case DT_REG:
setnsfpn(sub, both, RecurseNo);
break;
}
}
next += dir->d_reclen;
}
}
}
_syscall(fcntl(fd, F_SETPROTECTIONCLASS, 4));
}
return 0;
}
int main(int argc, const char *argv[]) {
return setnsfpn(argv[1], strlen(argv[1]), RecurseMaybe);
}