mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-05 19:31:57 -04:00
1446 lines
51 KiB
C
1446 lines
51 KiB
C
#include <linux/version.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/path.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/list.h>
|
|
#include <linux/init_task.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/version.h>
|
|
#include <linux/fdtable.h>
|
|
#include <linux/mnt_namespace.h>
|
|
#include "internal.h"
|
|
#include "mount.h"
|
|
#include <linux/susfs.h>
|
|
|
|
LIST_HEAD(LH_SUS_PATH);
|
|
LIST_HEAD(LH_SUS_KSTAT_SPOOFER);
|
|
LIST_HEAD(LH_SUS_MOUNT);
|
|
LIST_HEAD(LH_SUS_MAPS_SPOOFER);
|
|
LIST_HEAD(LH_SUS_PROC_FD_LINK);
|
|
LIST_HEAD(LH_SUS_MEMFD);
|
|
LIST_HEAD(LH_TRY_UMOUNT_PATH);
|
|
LIST_HEAD(LH_MOUNT_ID_RECORDER);
|
|
|
|
struct st_susfs_uname my_uname;
|
|
|
|
spinlock_t susfs_spin_lock;
|
|
spinlock_t susfs_mnt_id_recorder_spin_lock;
|
|
|
|
bool is_log_enable = true;
|
|
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
|
|
#define SUSFS_LOGI(fmt, ...) if (is_log_enable) pr_info("susfs:[%u][%u][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
|
|
#define SUSFS_LOGE(fmt, ...) if (is_log_enable) pr_err("susfs:[%u][%u][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
|
|
#else
|
|
#define SUSFS_LOGI(fmt, ...)
|
|
#define SUSFS_LOGE(fmt, ...)
|
|
#endif
|
|
|
|
int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info) {
|
|
struct st_susfs_sus_path_list *cursor, *temp;
|
|
struct st_susfs_sus_path_list *new_list = NULL;
|
|
struct st_susfs_sus_path info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_path))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_PATH, list) {
|
|
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
|
|
SUSFS_LOGE("target_pathname: '%s' is already created in LH_SUS_PATH\n", info.target_pathname);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_path_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_path));
|
|
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_PATH);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_pathname: '%s' is successfully added to LH_SUS_PATH\n", info.target_pathname);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_add_sus_mount(struct st_susfs_sus_mount* __user user_info) {
|
|
struct st_susfs_sus_mount_list *cursor, *temp;
|
|
struct st_susfs_sus_mount_list *new_list = NULL;
|
|
struct st_susfs_sus_mount info;
|
|
int list_count = 0;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_mount))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MOUNT, list) {
|
|
if (unlikely(!strcmp(cursor->info.target_pathname, info.target_pathname))) {
|
|
SUSFS_LOGE("target_pathname: '%s' is already created in LH_SUS_MOUNT\n", cursor->info.target_pathname);
|
|
return 1;
|
|
}
|
|
list_count++;
|
|
}
|
|
|
|
if (list_count == SUSFS_MAX_SUS_MNTS) {
|
|
SUSFS_LOGE("LH_SUS_MOUNT has reached the list limit of %d\n", SUSFS_MAX_SUS_MNTS);
|
|
return 1;
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_mount_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_mount));
|
|
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_MOUNT);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_pathname: '%s', is successfully added to LH_SUS_MOUNT\n", new_list->info.target_pathname);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info) {
|
|
struct st_susfs_sus_kstat_list *cursor, *temp;
|
|
struct st_susfs_sus_kstat_list *new_list = NULL;
|
|
struct st_susfs_sus_kstat info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_kstat))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_KSTAT_SPOOFER, list) {
|
|
if (cursor->info.target_ino == info.target_ino) {
|
|
if (info.target_pathname[0] != '\0') {
|
|
SUSFS_LOGE("target_pathname: '%s' is already created in LH_SUS_KSTAT_SPOOFER\n", info.target_pathname);
|
|
} else {
|
|
SUSFS_LOGE("target_ino: '%lu' is already created in LH_SUS_KSTAT_SPOOFER\n", info.target_ino);
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_kstat_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_kstat));
|
|
/* Seems the dev number issue is finally solved, the userspace stat we see is already a encoded dev
|
|
* which is set by new_encode_dev() / huge_encode_dev() function for 64bit system and
|
|
* old_encode_dev() for 32bit only system, that's why we need to decode it in kernel as well,
|
|
* and different kernel may have different function to encode the dev number, be cautious!
|
|
* Also check your encode_dev() macro in fs/stat.c to determine which one to use
|
|
*/
|
|
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
|
|
#ifdef CONFIG_MIPS
|
|
new_list->info.spoofed_dev = new_decode_dev(new_list->info.spoofed_dev);
|
|
#else
|
|
new_list->info.spoofed_dev = huge_decode_dev(new_list->info.spoofed_dev);
|
|
#endif /* CONFIG_MIPS */
|
|
#else
|
|
new_list->info.spoofed_dev = old_decode_dev(new_list->info.spoofed_dev);
|
|
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_KSTAT_SPOOFER);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s', spoofed_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', is successfully added to LH_SUS_KSTAT_SPOOFER\n",
|
|
new_list->info.target_ino , new_list->info.target_pathname, new_list->info.spoofed_pathname,
|
|
new_list->info.spoofed_ino, new_list->info.spoofed_dev, new_list->info.spoofed_nlink,
|
|
new_list->info.spoofed_atime_tv_sec, new_list->info.spoofed_mtime_tv_sec, new_list->info.spoofed_ctime_tv_sec,
|
|
new_list->info.spoofed_atime_tv_nsec, new_list->info.spoofed_mtime_tv_nsec, new_list->info.spoofed_ctime_tv_nsec);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_update_sus_kstat(struct st_susfs_sus_kstat* __user user_info) {
|
|
struct st_susfs_sus_kstat_list *cursor, *temp;
|
|
struct st_susfs_sus_kstat info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_kstat))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_KSTAT_SPOOFER, list) {
|
|
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
|
|
SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in LH_SUS_KSTAT_SPOOFER\n", cursor->info.target_ino, info.target_ino, info.target_pathname);
|
|
cursor->info.target_ino = info.target_ino;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
SUSFS_LOGE("target_pathname: '%s' is not found in LH_SUS_KSTAT_SPOOFER\n", info.target_pathname);
|
|
return 1;
|
|
}
|
|
|
|
int susfs_add_sus_maps(struct st_susfs_sus_maps* __user user_info) {
|
|
struct st_susfs_sus_maps_list *cursor, *temp;
|
|
struct st_susfs_sus_maps_list *new_list = NULL;
|
|
struct st_susfs_sus_maps info;
|
|
int list_count = 0;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_maps))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
|
|
#ifdef CONFIG_MIPS
|
|
info.target_dev = new_decode_dev(info.target_dev);
|
|
#else
|
|
info.target_dev = huge_decode_dev(info.target_dev);
|
|
#endif /* CONFIG_MIPS */
|
|
#else
|
|
info.target_dev = old_decode_dev(info.target_dev);
|
|
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MAPS_SPOOFER, list) {
|
|
if (cursor->info.is_statically == info.is_statically && !info.is_statically) {
|
|
if (cursor->info.target_ino == info.target_ino) {
|
|
SUSFS_LOGE("is_statically: '%d', target_ino: '%lu', is already created in LH_SUS_MAPS_SPOOFER\n",
|
|
info.is_statically, info.target_ino);
|
|
return 1;
|
|
}
|
|
} else if (cursor->info.is_statically == info.is_statically && info.is_statically) {
|
|
if (cursor->info.compare_mode == info.compare_mode && info.compare_mode == 1) {
|
|
if (cursor->info.target_ino == info.target_ino) {
|
|
SUSFS_LOGE("is_statically: '%d', compare_mode: '%d', target_ino: '%lu', is already created in LH_SUS_MAPS_SPOOFER\n",
|
|
info.is_statically, info.compare_mode, info.target_ino);
|
|
return 1;
|
|
}
|
|
} else if (cursor->info.compare_mode == info.compare_mode && info.compare_mode == 2) {
|
|
if (cursor->info.target_ino == info.target_ino &&
|
|
cursor->info.is_isolated_entry == info.is_isolated_entry &&
|
|
cursor->info.target_addr_size == info.target_addr_size &&
|
|
cursor->info.target_pgoff == info.target_pgoff &&
|
|
cursor->info.target_prot == info.target_prot) {
|
|
SUSFS_LOGE("is_statically: '%d', compare_mode: '%d', target_ino: '%lu', is_isolated_entry: '%d', target_pgoff: '0x%x', target_prot: '0x%x', is already created in LH_SUS_MAPS_SPOOFER\n",
|
|
info.is_statically, info.compare_mode, info.target_ino,
|
|
info.is_isolated_entry, info.target_pgoff, info.target_prot);
|
|
return 1;
|
|
}
|
|
} else if (cursor->info.compare_mode == info.compare_mode && info.compare_mode == 3) {
|
|
if (info.target_ino == 0 &&
|
|
cursor->info.prev_target_ino == info.prev_target_ino &&
|
|
cursor->info.next_target_ino == info.next_target_ino) {
|
|
SUSFS_LOGE("is_statically: '%d', compare_mode: '%d', target_ino: '%lu', prev_target_ino: '%lu', next_target_ino: '%lu', is already created in LH_SUS_MAPS_SPOOFER\n",
|
|
info.is_statically, info.compare_mode, info.target_ino,
|
|
info.prev_target_ino, info.next_target_ino);
|
|
return 1;
|
|
}
|
|
} else if (cursor->info.compare_mode == info.compare_mode && info.compare_mode == 4) {
|
|
if (cursor->info.is_file == info.is_file &&
|
|
cursor->info.target_dev == info.target_dev &&
|
|
cursor->info.target_pgoff == info.target_pgoff &&
|
|
cursor->info.target_prot == info.target_prot &&
|
|
cursor->info.target_addr_size == info.target_addr_size) {
|
|
SUSFS_LOGE("is_statically: '%d', compare_mode: '%d', is_file: '%d', target_dev: '0x%x', target_pgoff: '0x%x', target_prot: '0x%x', target_addr_size: '0x%x', is already created in LH_SUS_MAPS_SPOOFER\n",
|
|
info.is_statically, info.compare_mode, info.is_file,
|
|
info.target_dev, info.target_pgoff, info.target_prot,
|
|
info.target_addr_size);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
list_count++;
|
|
}
|
|
|
|
if (list_count == SUSFS_MAX_SUS_MAPS) {
|
|
SUSFS_LOGE("LH_SUS_MOUNT has reached the list limit of %d\n", SUSFS_MAX_SUS_MAPS);
|
|
return 1;
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_maps_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_maps));
|
|
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
|
|
#ifdef CONFIG_MIPS
|
|
new_list->info.spoofed_dev = new_decode_dev(new_list->info.spoofed_dev);
|
|
#else
|
|
new_list->info.spoofed_dev = huge_decode_dev(new_list->info.spoofed_dev);
|
|
#endif /* CONFIG_MIPS */
|
|
#else
|
|
new_list->info.spoofed_dev = old_decode_dev(new_list->info.spoofed_dev);
|
|
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_MAPS_SPOOFER);
|
|
spin_unlock(&susfs_spin_lock);
|
|
|
|
SUSFS_LOGI("is_statically: '%d', compare_mode: '%d', is_isolated_entry: '%d', is_file: '%d', prev_target_ino: '%lu', next_target_ino: '%lu', target_ino: '%lu', target_dev: '0x%x', target_pgoff: '0x%x', target_prot: '0x%x', target_addr_size: '0x%x', spoofed_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '0x%x', spoofed_pgoff: '0x%x', spoofed_prot: '0x%x', is successfully added to LH_SUS_MAPS_SPOOFER\n",
|
|
new_list->info.is_statically, new_list->info.compare_mode, new_list->info.is_isolated_entry,
|
|
new_list->info.is_file, new_list->info.prev_target_ino, new_list->info.next_target_ino,
|
|
new_list->info.target_ino, new_list->info.target_dev, new_list->info.target_pgoff,
|
|
new_list->info.target_prot, new_list->info.target_addr_size, new_list->info.spoofed_pathname,
|
|
new_list->info.spoofed_ino, new_list->info.spoofed_dev, new_list->info.spoofed_pgoff,
|
|
new_list->info.spoofed_prot);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int susfs_update_sus_maps(struct st_susfs_sus_maps* __user user_info) {
|
|
struct st_susfs_sus_maps_list *cursor, *temp;
|
|
struct st_susfs_sus_maps info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_maps))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MAPS_SPOOFER, list) {
|
|
if (cursor->info.is_statically == info.is_statically && !info.is_statically) {
|
|
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
|
|
SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in LH_SUS_MAPS_SPOOFER\n", cursor->info.target_ino, info.target_ino, info.target_pathname);
|
|
cursor->info.target_ino = info.target_ino;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
SUSFS_LOGE("target_pathname: '%s' is not found in LH_SUS_MAPS_SPOOFER\n", info.target_pathname);
|
|
return 1;
|
|
}
|
|
|
|
int susfs_add_sus_proc_fd_link(struct st_susfs_sus_proc_fd_link* __user user_info) {
|
|
struct st_susfs_sus_proc_fd_link_list *cursor, *temp;
|
|
struct st_susfs_sus_proc_fd_link_list *new_list = NULL;
|
|
struct st_susfs_sus_proc_fd_link info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_proc_fd_link))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_PROC_FD_LINK, list) {
|
|
if (unlikely(!strcmp(info.target_link_name, cursor->info.target_link_name))) {
|
|
SUSFS_LOGE("target_link_name: '%s' is already created in LH_SUS_PROC_FD_LINK\n", info.target_link_name);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_proc_fd_link_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_proc_fd_link));
|
|
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_PROC_FD_LINK);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_link_name: '%s', spoofed_link_name: '%s', is successfully added to LH_SUS_PROC_FD_LINK\n",
|
|
new_list->info.target_link_name, new_list->info.spoofed_link_name);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_add_sus_memfd(struct st_susfs_sus_memfd* __user user_info) {
|
|
struct st_susfs_sus_memfd_list *cursor, *temp;
|
|
struct st_susfs_sus_memfd_list *new_list = NULL;
|
|
struct st_susfs_sus_memfd info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_sus_memfd))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MEMFD, list) {
|
|
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
|
|
SUSFS_LOGE("target_pathname: '%s' is already created in LH_SUS_MEMFD\n", info.target_pathname);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_sus_memfd_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_sus_memfd));
|
|
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_SUS_MEMFD);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_pathname: '%s', is successfully added to LH_SUS_MEMFD\n",
|
|
new_list->info.target_pathname);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_add_try_umount(struct st_susfs_try_umount* __user user_info) {
|
|
struct st_susfs_try_umount_list *cursor, *temp;
|
|
struct st_susfs_try_umount_list *new_list = NULL;
|
|
struct st_susfs_try_umount info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_try_umount))) {
|
|
SUSFS_LOGE("failed copying from userspace\n");
|
|
return 1;
|
|
}
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) {
|
|
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
|
|
SUSFS_LOGE("target_pathname: '%s' is already created in LH_TRY_UMOUNT_PATH\n", info.target_pathname);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
new_list = kmalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL);
|
|
if (!new_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 1;
|
|
}
|
|
|
|
memcpy(&new_list->info, &info, sizeof(struct st_susfs_try_umount));
|
|
|
|
INIT_LIST_HEAD(&new_list->list);
|
|
spin_lock(&susfs_spin_lock);
|
|
list_add_tail(&new_list->list, &LH_TRY_UMOUNT_PATH);
|
|
spin_unlock(&susfs_spin_lock);
|
|
SUSFS_LOGI("target_pathname: '%s', mnt_mode: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", new_list->info.target_pathname, new_list->info.mnt_mode);
|
|
return 0;
|
|
}
|
|
|
|
int susfs_set_uname(struct st_susfs_uname* __user user_info) {
|
|
struct st_susfs_uname info;
|
|
|
|
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_uname))) {
|
|
SUSFS_LOGE("failed copying from userspace.\n");
|
|
return 1;
|
|
}
|
|
|
|
spin_lock(&susfs_spin_lock);
|
|
strncpy(my_uname.sysname, info.sysname, __NEW_UTS_LEN);
|
|
strncpy(my_uname.nodename, info.nodename, __NEW_UTS_LEN);
|
|
strncpy(my_uname.release, info.release, __NEW_UTS_LEN);
|
|
strncpy(my_uname.version, info.version, __NEW_UTS_LEN);
|
|
strncpy(my_uname.machine, info.machine, __NEW_UTS_LEN);
|
|
SUSFS_LOGI("setting sysname: '%s', nodename: '%s', release: '%s', version: '%s', machine: '%s'\n",
|
|
my_uname.sysname, my_uname.nodename, my_uname.release, my_uname.version, my_uname.machine);
|
|
spin_unlock(&susfs_spin_lock);
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
|
|
int susfs_sus_path_by_path(struct path* file, int* errno_to_be_changed, int syscall_family)
|
|
#else
|
|
int susfs_sus_path_by_path(const struct path* file, int* errno_to_be_changed, int syscall_family)
|
|
#endif
|
|
{
|
|
int res = 0;
|
|
int status = 0;
|
|
char* path = NULL;
|
|
char* ptr = NULL;
|
|
char* end = NULL;
|
|
struct st_susfs_sus_path_list *cursor, *temp;
|
|
|
|
if (!uid_matches_suspicious_path() || file == NULL) {
|
|
return status;
|
|
}
|
|
|
|
path = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
if (path == NULL) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return status;
|
|
}
|
|
ptr = d_path(file, path, PAGE_SIZE);
|
|
if (IS_ERR(ptr)) {
|
|
SUSFS_LOGE("d_path() failed\n");
|
|
goto out_free_path;
|
|
}
|
|
end = mangle_path(path, ptr, " \t\n\\");
|
|
if (!end) {
|
|
goto out_free_path;
|
|
}
|
|
res = end - path;
|
|
path[(size_t) res] = '\0';
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_PATH, list) {
|
|
if (unlikely(!strcmp(cursor->info.target_pathname, path))) {
|
|
SUSFS_LOGI("hiding target_pathname: '%s', target_ino: '%lu'\n", cursor->info.target_pathname, cursor->info.target_ino);
|
|
if (errno_to_be_changed != NULL) {
|
|
susfs_change_error_no_by_pathname(path, errno_to_be_changed, syscall_family);
|
|
}
|
|
status = 1;
|
|
goto out_free_path;
|
|
}
|
|
}
|
|
|
|
out_free_path:
|
|
kfree(path);
|
|
return status;
|
|
}
|
|
|
|
int susfs_sus_path_by_filename(struct filename* name, int* errno_to_be_changed, int syscall_family) {
|
|
int status = 0;
|
|
int ret = 0;
|
|
struct path path;
|
|
|
|
if (IS_ERR(name)) {
|
|
return status;
|
|
}
|
|
|
|
if (!uid_matches_suspicious_path() || name == NULL) {
|
|
return status;
|
|
}
|
|
|
|
ret = kern_path(name->name, LOOKUP_FOLLOW, &path);
|
|
|
|
if (!ret) {
|
|
status = susfs_sus_path_by_path(&path, errno_to_be_changed, syscall_family);
|
|
path_put(&path);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int susfs_sus_ino_for_filldir64(unsigned long ino) {
|
|
struct st_susfs_sus_path_list *cursor, *temp;
|
|
|
|
if (!uid_matches_suspicious_path())
|
|
return 0;
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_PATH, list) {
|
|
if (cursor->info.target_ino == ino) {
|
|
SUSFS_LOGI("hiding target_pathname: '%s', target_ino: '%lu'\n", cursor->info.target_pathname, cursor->info.target_ino);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int susfs_sus_mount(struct vfsmount* mnt, struct path* root) {
|
|
struct st_susfs_sus_mount_list *cursor, *temp;
|
|
char* path = NULL;
|
|
char* ptr = NULL;
|
|
char* end = NULL;
|
|
int res = 0;
|
|
int status = 0;
|
|
struct path mnt_path = {
|
|
.dentry = mnt->mnt_root,
|
|
.mnt = mnt
|
|
};
|
|
|
|
path = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
if (path == NULL) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return 0;
|
|
}
|
|
ptr = __d_path(&mnt_path, root, path, PAGE_SIZE);
|
|
if (IS_ERR(ptr)) {
|
|
SUSFS_LOGE("__d_path() failed\n");
|
|
goto out_free_path;
|
|
}
|
|
end = mangle_path(path, ptr, " \t\n\\");
|
|
if (!end) {
|
|
goto out_free_path;
|
|
}
|
|
res = end - path;
|
|
path[(size_t) res] = '\0';
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MOUNT, list) {
|
|
if (unlikely(!strcmp(path, cursor->info.target_pathname))) {
|
|
SUSFS_LOGI("hide target_pathname '%s' from mounts\n",
|
|
cursor->info.target_pathname);
|
|
status = 1;
|
|
goto out_free_path;
|
|
}
|
|
}
|
|
out_free_path:
|
|
kfree(path);
|
|
return status;
|
|
}
|
|
|
|
/* This function records the original mnt_id and parent_mnt_id of all mounts of
|
|
* current process and save to a list of corresponding spoofed mnt_id and parent_mnt_id
|
|
* once process with uid >= 10000 opens /proc/self/mountinfo
|
|
*/
|
|
void susfs_add_mnt_id_recorder(struct mnt_namespace *ns) {
|
|
struct st_susfs_mnt_id_recorder_list *new_recorder_list = NULL;
|
|
struct st_susfs_mnt_id_recorder_list *recorder_cursor, *recorder_temp;
|
|
struct st_susfs_sus_mount_list *sus_mount_cursor, *sus_mount_temp;
|
|
struct mount *mnt_cursor, *mnt_temp;
|
|
struct path mnt_path;
|
|
char *path = NULL;
|
|
char *p_path = NULL;
|
|
char *end = NULL;
|
|
int res = 0;
|
|
int cur_pid = current->pid;
|
|
int i = 0, count = 0;
|
|
|
|
if (!ns)
|
|
return;
|
|
|
|
// if there exists the same pid already, increase the reference
|
|
list_for_each_entry_safe(recorder_cursor, recorder_temp, &LH_MOUNT_ID_RECORDER, list) {
|
|
if (recorder_cursor->pid == cur_pid) {
|
|
recorder_cursor->opened_count++;
|
|
SUSFS_LOGI("mountinfo opened by the same pid: '%d', recorder_cursor->opened_count: '%d'\n",
|
|
cur_pid, recorder_cursor->opened_count);
|
|
return;
|
|
}
|
|
}
|
|
|
|
new_recorder_list = kzalloc(sizeof(struct st_susfs_mnt_id_recorder_list), GFP_KERNEL);
|
|
if (!new_recorder_list) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
return;
|
|
}
|
|
new_recorder_list->info.count = 0;
|
|
|
|
path = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
if (!path) {
|
|
SUSFS_LOGE("no enough memory\n");
|
|
goto out_free_new_recorder_list;
|
|
}
|
|
|
|
list_for_each_entry_safe(mnt_cursor, mnt_temp, &ns->list, mnt_list) {
|
|
// Avoid overflow
|
|
if (count == SUSFS_MAX_SUS_MNTS) {
|
|
SUSFS_LOGE("LH_MOUNT_ID_RECORDER has reached the list limit of %d\n", SUSFS_MAX_SUS_MNTS);
|
|
goto out_free_path;
|
|
}
|
|
// if this is the first mount entry
|
|
if (count == 0) {
|
|
new_recorder_list->info.target_mnt_id[count] = mnt_cursor->mnt_id;
|
|
new_recorder_list->info.spoofed_mnt_id[count] = mnt_cursor->mnt_id;
|
|
new_recorder_list->info.spoofed_parent_mnt_id[count] = mnt_cursor->mnt_parent->mnt_id;
|
|
new_recorder_list->info.count = ++count;
|
|
continue;
|
|
}
|
|
|
|
mntget(&mnt_cursor->mnt);
|
|
dget(mnt_cursor->mnt.mnt_root);
|
|
mnt_path.mnt = &mnt_cursor->mnt;
|
|
mnt_path.dentry = mnt_cursor->mnt.mnt_root;
|
|
|
|
p_path = d_path(&mnt_path, path, PAGE_SIZE);
|
|
if (IS_ERR(p_path)) {
|
|
SUSFS_LOGE("d_path() failed\n");
|
|
goto out_continue;
|
|
}
|
|
end = mangle_path(path, p_path, " \t\n\\");
|
|
if (!end) {
|
|
goto out_continue;
|
|
}
|
|
res = end - path;
|
|
path[(size_t) res] = '\0';
|
|
|
|
// check if the mount is suspicious
|
|
list_for_each_entry_safe(sus_mount_cursor, sus_mount_temp, &LH_SUS_MOUNT, list) {
|
|
// skip adding this mount to recorder list if it is suspicious
|
|
if (unlikely(!strcmp(path, sus_mount_cursor->info.target_pathname))) {
|
|
SUSFS_LOGI("skip adding target_mnt_id: '%d', target_pathname: '%s' to LH_MOUNT_ID_RECORDER\n",
|
|
mnt_cursor->mnt_id, sus_mount_cursor->info.target_pathname);
|
|
goto out_continue;
|
|
}
|
|
}
|
|
// if the mount entry is NOT suspicioius
|
|
new_recorder_list->info.target_mnt_id[count] = mnt_cursor->mnt_id;
|
|
new_recorder_list->info.spoofed_mnt_id[count] = new_recorder_list->info.spoofed_mnt_id[0] + count;
|
|
for (i = 0; i < count; i++) {
|
|
if (mnt_cursor->mnt_parent->mnt_id == new_recorder_list->info.target_mnt_id[i]) {
|
|
new_recorder_list->info.spoofed_parent_mnt_id[count] = new_recorder_list->info.spoofed_mnt_id[i];
|
|
break;
|
|
}
|
|
}
|
|
// if no match from above, use the original parent mnt_id
|
|
if (new_recorder_list->info.spoofed_parent_mnt_id[count] == 0) {
|
|
new_recorder_list->info.spoofed_parent_mnt_id[count] = mnt_cursor->mnt_parent->mnt_id;
|
|
}
|
|
new_recorder_list->info.count = ++count;
|
|
out_continue:
|
|
dput(mnt_cursor->mnt.mnt_root);
|
|
mntput(&mnt_cursor->mnt);
|
|
}
|
|
|
|
new_recorder_list->pid = cur_pid;
|
|
new_recorder_list->opened_count = 1;
|
|
kfree(path);
|
|
|
|
/*
|
|
for (i = 0; i<new_recorder_list->info.count; i++) {
|
|
SUSFS_LOGI("target_mnt_id: %d, spoofed_mnt_id: %d, spoofed_parent_mnt_id: %d\n",
|
|
new_recorder_list->info.target_mnt_id[i],
|
|
new_recorder_list->info.spoofed_mnt_id[i],
|
|
new_recorder_list->info.spoofed_parent_mnt_id[i]);
|
|
}
|
|
*/
|
|
|
|
INIT_LIST_HEAD(&new_recorder_list->list);
|
|
spin_lock(&susfs_mnt_id_recorder_spin_lock);
|
|
list_add_tail(&new_recorder_list->list, &LH_MOUNT_ID_RECORDER);
|
|
spin_unlock(&susfs_mnt_id_recorder_spin_lock);
|
|
SUSFS_LOGI("recording pid '%u' to LH_MOUNT_ID_RECORDER\n", new_recorder_list->pid);
|
|
return;
|
|
out_free_path:
|
|
kfree(path);
|
|
out_free_new_recorder_list:
|
|
kfree(new_recorder_list);
|
|
}
|
|
|
|
int susfs_get_fake_mnt_id(int mnt_id, int *out_mnt_id, int *out_parent_mnt_id) {
|
|
struct st_susfs_mnt_id_recorder_list *cursor, *temp;
|
|
int cur_pid = current->pid;
|
|
int i;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_MOUNT_ID_RECORDER, list) {
|
|
if (cursor->pid == cur_pid) {
|
|
for (i = 0; i < cursor->info.count; i++) {
|
|
if (cursor->info.target_mnt_id[i] == mnt_id) {
|
|
*out_mnt_id = cursor->info.spoofed_mnt_id[i];
|
|
*out_parent_mnt_id = cursor->info.spoofed_parent_mnt_id[i];
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void susfs_remove_mnt_id_recorder(void) {
|
|
struct st_susfs_mnt_id_recorder_list *cursor, *temp;
|
|
int cur_pid = current->pid;
|
|
|
|
spin_lock(&susfs_mnt_id_recorder_spin_lock);
|
|
list_for_each_entry_safe(cursor, temp, &LH_MOUNT_ID_RECORDER, list) {
|
|
if (cursor->pid == cur_pid) {
|
|
cursor->opened_count--;
|
|
if (cursor->opened_count != 0)
|
|
goto out_spin_unlock;
|
|
list_del(&cursor->list);
|
|
kfree(cursor);
|
|
SUSFS_LOGI("removing pid '%u' from LH_MOUNT_ID_RECORDER\n", cur_pid);
|
|
goto out_spin_unlock;
|
|
}
|
|
}
|
|
out_spin_unlock:
|
|
spin_unlock(&susfs_mnt_id_recorder_spin_lock);
|
|
}
|
|
|
|
void susfs_sus_kstat(unsigned long ino, struct stat* out_stat) {
|
|
struct st_susfs_sus_kstat_list *cursor, *temp;
|
|
|
|
if (!uid_matches_suspicious_kstat())
|
|
return;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_KSTAT_SPOOFER, list) {
|
|
if (cursor->info.target_ino == ino) {
|
|
SUSFS_LOGI("spoofing kstat for pathname '%s' for UID %i\n", cursor->info.target_pathname, current_uid().val);
|
|
out_stat->st_ino = cursor->info.spoofed_ino;
|
|
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
|
|
#ifdef CONFIG_MIPS
|
|
out_stat->st_dev = new_encode_dev(cursor->info.spoofed_dev);
|
|
#else
|
|
out_stat->st_dev = huge_encode_dev(cursor->info.spoofed_dev);
|
|
#endif /* CONFIG_MIPS */
|
|
#else
|
|
out_stat->st_dev = old_encode_dev(cursor->info.spoofed_dev);
|
|
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
|
|
out_stat->st_nlink = cursor->info.spoofed_nlink;
|
|
out_stat->st_atime = cursor->info.spoofed_atime_tv_sec;
|
|
out_stat->st_mtime = cursor->info.spoofed_mtime_tv_sec;
|
|
out_stat->st_ctime = cursor->info.spoofed_ctime_tv_sec;
|
|
#ifdef _STRUCT_TIMESPEC
|
|
out_stat->st_atime_nsec = cursor->info.spoofed_atime_tv_nsec;
|
|
out_stat->st_mtime_nsec = cursor->info.spoofed_mtime_tv_nsec;
|
|
out_stat->st_ctime_nsec = cursor->info.spoofed_ctime_tv_nsec;
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* for non statically, it only compare with target_ino, and spoof only the ino, dev to the matched entry
|
|
* for staticially, it compares depending on the mode user chooses
|
|
* compare mode:
|
|
* 1 -> target_ino is 'non-zero', all entries match with target_ino will be spoofed with user defined entry
|
|
* 2 -> target_ino is 'non-zero', all entries match with [target_ino,target_addr_size,target_prot,target_pgoff,is_isolated_entry] will be spoofed with user defined entry
|
|
* 3 -> target_ino is 'zero', which is not file, all entries match with [prev_target_ino,next_target_ino] will be spoofed with user defined entry
|
|
* 4 -> target_ino is 'zero' or 'non-zero', all entries match with [is_file,target_addr_size,target_prot,target_pgoff,target_dev] will be spoofed with user defined entry
|
|
*/
|
|
int susfs_sus_maps(unsigned long target_ino, unsigned long target_addr_size, unsigned long* orig_ino, dev_t* orig_dev, vm_flags_t* flags, unsigned long long* pgoff, struct vm_area_struct* vma, char* out_name) {
|
|
struct st_susfs_sus_maps_list *cursor, *temp;
|
|
struct inode *tmp_inode, *tmp_inode_prev, *tmp_inode_next;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MAPS_SPOOFER, list) {
|
|
// if it is NOT statically
|
|
if (!cursor->info.is_statically) {
|
|
if (target_ino != 0 && cursor->info.target_ino == target_ino) {
|
|
*orig_ino = cursor->info.spoofed_ino;
|
|
*orig_dev = cursor->info.spoofed_dev;
|
|
SUSFS_LOGI("spoofing maps -> is_statically: '%d', compare_mode: '%d', is_file: '%d', is_isolated_entry: '%d', prev_target_ino: '%lu', next_target_ino: '%lu', target_ino: '%lu', target_dev: '0x%x', target_pgoff: '0x%x', target_prot: '0x%x', target_addr_size: '0x%x', spoofed_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '0x%x', spoofed_pgoff: '0x%x', spoofed_prot: '0x%x'\n",
|
|
cursor->info.is_statically, cursor->info.compare_mode, cursor->info.is_file,
|
|
cursor->info.is_isolated_entry, cursor->info.prev_target_ino, cursor->info.next_target_ino,
|
|
cursor->info.target_ino, cursor->info.target_dev, cursor->info.target_pgoff,
|
|
cursor->info.target_prot, cursor->info.target_addr_size, cursor->info.spoofed_pathname,
|
|
cursor->info.spoofed_ino, cursor->info.spoofed_dev, cursor->info.spoofed_pgoff,
|
|
cursor->info.spoofed_prot);
|
|
return 1;
|
|
}
|
|
// if it is statically, then compare with compare_mode
|
|
} else if (cursor->info.compare_mode > 0) {
|
|
switch(cursor->info.compare_mode) {
|
|
case 1:
|
|
if (target_ino != 0 && cursor->info.target_ino == target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (target_ino != 0 && cursor->info.target_ino == target_ino &&
|
|
((cursor->info.target_prot & VM_READ) == (*flags & VM_READ)) &&
|
|
((cursor->info.target_prot & VM_WRITE) == (*flags & VM_WRITE)) &&
|
|
((cursor->info.target_prot & VM_EXEC) == (*flags & VM_EXEC)) &&
|
|
((cursor->info.target_prot & VM_MAYSHARE) == (*flags & VM_MAYSHARE)) &&
|
|
cursor->info.target_addr_size == target_addr_size &&
|
|
cursor->info.target_pgoff == *pgoff) {
|
|
// if is NOT isolated_entry, check for vma->vm_next and vma->vm_prev to see if they have the same inode
|
|
if (!cursor->info.is_isolated_entry) {
|
|
if (vma && vma->vm_next) {
|
|
if (vma->vm_next->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino)
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
if (vma && vma->vm_prev) {
|
|
if (vma->vm_prev->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino)
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
continue;
|
|
// if it is isolated_entry
|
|
} else {
|
|
if (vma && vma->vm_next) {
|
|
if (vma->vm_next->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (vma && vma->vm_prev) {
|
|
if (vma->vm_prev->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
// both prev and next don't have the same indoe as current entry, we can spoof now
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
// if current vma is a file, it is not our target
|
|
if (vma->vm_file) continue;
|
|
// compare next target ino only
|
|
if (cursor->info.prev_target_ino == 0 && cursor->info.next_target_ino > 0) {
|
|
if (vma->vm_next && vma->vm_next->vm_file) {
|
|
tmp_inode_next = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode_next->i_ino == cursor->info.next_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
// compare prev target ino only
|
|
} else if (cursor->info.prev_target_ino > 0 && cursor->info.next_target_ino == 0) {
|
|
if (vma->vm_prev && vma->vm_prev->vm_file) {
|
|
tmp_inode_prev = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode_prev->i_ino == cursor->info.prev_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
// compare both prev ino and next ino
|
|
} else if (cursor->info.prev_target_ino > 0 && cursor->info.next_target_ino > 0) {
|
|
if (vma->vm_prev && vma->vm_prev->vm_file &&
|
|
vma->vm_next && vma->vm_next->vm_file) {
|
|
tmp_inode_prev = file_inode(vma->vm_prev->vm_file);
|
|
tmp_inode_next = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode_prev->i_ino == cursor->info.prev_target_ino &&
|
|
tmp_inode_next->i_ino == cursor->info.next_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 4:
|
|
if ((cursor->info.is_file && vma->vm_file)||(!cursor->info.is_file && !vma->vm_file)) {
|
|
if (cursor->info.target_dev == *orig_dev &&
|
|
cursor->info.target_pgoff == *pgoff &&
|
|
((cursor->info.target_prot & VM_READ) == (*flags & VM_READ) &&
|
|
(cursor->info.target_prot & VM_WRITE) == (*flags & VM_WRITE) &&
|
|
(cursor->info.target_prot & VM_EXEC) == (*flags & VM_EXEC) &&
|
|
(cursor->info.target_prot & VM_MAYSHARE) == (*flags & VM_MAYSHARE)) &&
|
|
cursor->info.target_addr_size == target_addr_size) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
do_spoof:
|
|
if (cursor->info.need_to_spoof_pathname) {
|
|
strncpy(out_name, cursor->info.spoofed_pathname, SUSFS_MAX_LEN_PATHNAME-1);
|
|
}
|
|
if (cursor->info.need_to_spoof_ino) {
|
|
*orig_ino = cursor->info.spoofed_ino;
|
|
}
|
|
if (cursor->info.need_to_spoof_dev) {
|
|
*orig_dev = cursor->info.spoofed_dev;
|
|
}
|
|
if (cursor->info.need_to_spoof_prot) {
|
|
if (cursor->info.spoofed_prot & VM_READ) *flags |= VM_READ;
|
|
else *flags = ((*flags | VM_READ) ^ VM_READ);
|
|
if (cursor->info.spoofed_prot & VM_WRITE) *flags |= VM_WRITE;
|
|
else *flags = ((*flags | VM_WRITE) ^ VM_WRITE);
|
|
if (cursor->info.spoofed_prot & VM_EXEC) *flags |= VM_EXEC;
|
|
else *flags = ((*flags | VM_EXEC) ^ VM_EXEC);
|
|
if (cursor->info.spoofed_prot & VM_MAYSHARE) *flags |= VM_MAYSHARE;
|
|
else *flags = ((*flags | VM_MAYSHARE) ^ VM_MAYSHARE);
|
|
}
|
|
if (cursor->info.need_to_spoof_pgoff) {
|
|
*pgoff = cursor->info.spoofed_pgoff;
|
|
}
|
|
SUSFS_LOGI("spoofing maps -> is_statically: '%d', compare_mode: '%d', is_file: '%d', is_isolated_entry: '%d', prev_target_ino: '%lu', next_target_ino: '%lu', target_ino: '%lu', target_dev: '0x%x', target_pgoff: '0x%x', target_prot: '0x%x', target_addr_size: '0x%x', spoofed_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '0x%x', spoofed_pgoff: '0x%x', spoofed_prot: '0x%x'\n",
|
|
cursor->info.is_statically, cursor->info.compare_mode, cursor->info.is_file,
|
|
cursor->info.is_isolated_entry, cursor->info.prev_target_ino, cursor->info.next_target_ino,
|
|
cursor->info.target_ino, cursor->info.target_dev, cursor->info.target_pgoff,
|
|
cursor->info.target_prot, cursor->info.target_addr_size, cursor->info.spoofed_pathname,
|
|
cursor->info.spoofed_ino, cursor->info.spoofed_dev, cursor->info.spoofed_pgoff,
|
|
cursor->info.spoofed_prot);
|
|
/*
|
|
* show_map_vma() treats return value 2 as a request to print
|
|
* out_name and skip normal pathname handling. Only return 2
|
|
* when SUSFS actually supplied a spoofed pathname.
|
|
*
|
|
* For metadata-only spoofing, return 1 so the modified maps
|
|
* fields are retained while seq_file_path() still prints the
|
|
* original mapping pathname.
|
|
*/
|
|
return cursor->info.need_to_spoof_pathname ? 2 : 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* @ This function only does the following:
|
|
* 1. Spoof the symlink name of a target_ino listed in /proc/self/map_files
|
|
*
|
|
* @Note
|
|
* - It has limitation as there is no way to check which
|
|
* vma address it belongs by passing dentry* only, so it just
|
|
* checks for matched dentry* and its target_ino in sus_maps list,
|
|
* then spoof the symlink name of the target_ino defined by user.
|
|
* - Also user cannot see the effects in map_files from other root session,
|
|
* because it uses current->mm to compare the dentry, the only way to test
|
|
* is to check within its own pid.
|
|
* - So the BEST practise here is:
|
|
* Do NOT spoof the map entries which share the same name to different name
|
|
* seperately unless the other spoofed name is empty of which spoofed_ino is 0,
|
|
* otherwise there will be inconsistent entries between maps and map_files.
|
|
*/
|
|
void susfs_sus_map_files_readlink(unsigned long target_ino, char* pathname) {
|
|
struct st_susfs_sus_maps_list *cursor, *temp;
|
|
|
|
if (!pathname)
|
|
return;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MAPS_SPOOFER, list) {
|
|
// We are only interested in statically and target_ino > 0
|
|
if (cursor->info.is_statically && cursor->info.compare_mode > 0 &&
|
|
target_ino > 0 && cursor->info.target_ino == target_ino)
|
|
{
|
|
if (cursor->info.need_to_spoof_pathname) {
|
|
SUSFS_LOGI("spoofing symlink name of ino '%lu' to '%s' in map_files\n",
|
|
target_ino, cursor->info.spoofed_pathname);
|
|
// Don't need to check buffer size as 'pathname' is allocated with 'PAGE_SIZE'
|
|
// which is way bigger than SUSFS_MAX_LEN_PATHNAME
|
|
strcpy(pathname, cursor->info.spoofed_pathname);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* @ This function mainly does the following:
|
|
* 1. Remove the user write access for spoofed symlink name in /proc/self/map_files
|
|
* 2. Prevent the dentry from being seen in /proc/self/map_files
|
|
*
|
|
* @Note
|
|
* - anon files are supposed to be not shown in /proc/self/map_files and
|
|
* spoofing from memfd name to non-memfd name should not have write
|
|
* permission on that target dentry
|
|
*/
|
|
int susfs_sus_map_files_instantiate(struct vm_area_struct* vma) {
|
|
struct inode *inode = file_inode(vma->vm_file);
|
|
unsigned long target_ino = inode->i_ino;
|
|
dev_t target_dev = inode->i_sb->s_dev;
|
|
unsigned long long target_pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
|
|
unsigned long target_addr_size = vma->vm_end - vma->vm_start;
|
|
vm_flags_t target_flags = vma->vm_flags;
|
|
struct st_susfs_sus_maps_list *cursor, *temp;
|
|
struct inode *tmp_inode, *tmp_inode_prev, *tmp_inode_next;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MAPS_SPOOFER, list) {
|
|
// We are only interested in statically
|
|
if (!cursor->info.is_statically) {
|
|
continue;
|
|
// if it is statically, then compare with compare_mode
|
|
} else if (cursor->info.compare_mode > 0) {
|
|
switch(cursor->info.compare_mode) {
|
|
case 1:
|
|
if (target_ino != 0 && cursor->info.target_ino == target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (target_ino != 0 && cursor->info.target_ino == target_ino &&
|
|
((cursor->info.target_prot & VM_READ) == (target_flags & VM_READ)) &&
|
|
((cursor->info.target_prot & VM_WRITE) == (target_flags & VM_WRITE)) &&
|
|
((cursor->info.target_prot & VM_EXEC) == (target_flags & VM_EXEC)) &&
|
|
((cursor->info.target_prot & VM_MAYSHARE) == (target_flags & VM_MAYSHARE)) &&
|
|
cursor->info.target_addr_size == target_addr_size &&
|
|
cursor->info.target_pgoff == target_pgoff) {
|
|
// if is NOT isolated_entry, check for vma->vm_next and vma->vm_prev to see if they have the same inode
|
|
if (!cursor->info.is_isolated_entry) {
|
|
if (vma && vma->vm_next) {
|
|
if (vma->vm_next->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino)
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
if (vma && vma->vm_prev) {
|
|
if (vma->vm_prev->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino)
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
continue;
|
|
// if it is isolated_entry
|
|
} else {
|
|
if (vma && vma->vm_next) {
|
|
if (vma->vm_next->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (vma && vma->vm_prev) {
|
|
if (vma->vm_prev->vm_file) {
|
|
tmp_inode = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode->i_ino == cursor->info.target_ino) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
// both prev and next don't have the same indoe as current entry, we can spoof now
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
// if current vma is a file, it is not our target
|
|
if (vma->vm_file) continue;
|
|
// compare next target ino only
|
|
if (cursor->info.prev_target_ino == 0 && cursor->info.next_target_ino > 0) {
|
|
if (vma->vm_next && vma->vm_next->vm_file) {
|
|
tmp_inode_next = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode_next->i_ino == cursor->info.next_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
// compare prev target ino only
|
|
} else if (cursor->info.prev_target_ino > 0 && cursor->info.next_target_ino == 0) {
|
|
if (vma->vm_prev && vma->vm_prev->vm_file) {
|
|
tmp_inode_prev = file_inode(vma->vm_prev->vm_file);
|
|
if (tmp_inode_prev->i_ino == cursor->info.prev_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
// compare both prev ino and next ino
|
|
} else if (cursor->info.prev_target_ino > 0 && cursor->info.next_target_ino > 0) {
|
|
if (vma->vm_prev && vma->vm_prev->vm_file &&
|
|
vma->vm_next && vma->vm_next->vm_file) {
|
|
tmp_inode_prev = file_inode(vma->vm_prev->vm_file);
|
|
tmp_inode_next = file_inode(vma->vm_next->vm_file);
|
|
if (tmp_inode_prev->i_ino == cursor->info.prev_target_ino &&
|
|
tmp_inode_next->i_ino == cursor->info.next_target_ino) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 4:
|
|
if ((cursor->info.is_file && vma->vm_file)||(!cursor->info.is_file && !vma->vm_file)) {
|
|
if (cursor->info.target_dev == target_dev &&
|
|
cursor->info.target_pgoff == target_pgoff &&
|
|
((cursor->info.target_prot & VM_READ) == (target_flags & VM_READ) &&
|
|
(cursor->info.target_prot & VM_WRITE) == (target_flags & VM_WRITE) &&
|
|
(cursor->info.target_prot & VM_EXEC) == (target_flags & VM_EXEC) &&
|
|
(cursor->info.target_prot & VM_MAYSHARE) == (target_flags & VM_MAYSHARE)) &&
|
|
cursor->info.target_addr_size == target_addr_size) {
|
|
goto do_spoof;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
continue;
|
|
do_spoof:
|
|
if (!(cursor->info.spoofed_ino == 0 ||
|
|
(MAJOR(cursor->info.spoofed_dev) == 0 &&
|
|
(MINOR(cursor->info.spoofed_dev) == 0 || MINOR(cursor->info.spoofed_dev) == 1))))
|
|
{
|
|
SUSFS_LOGI("remove user write permission of spoofed symlink '%s' in map_files\n", cursor->info.spoofed_pathname);
|
|
return 1;
|
|
} else {
|
|
SUSFS_LOGI("drop dentry of target_ino '%lu' with spoofed_ino '%lu' in map_files\n",
|
|
cursor->info.target_ino, cursor->info.spoofed_ino);
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int susfs_is_sus_maps_list_empty(void) {
|
|
return list_empty(&LH_SUS_MAPS_SPOOFER);
|
|
}
|
|
|
|
int susfs_sus_proc_fd_link(char *pathname, int len) {
|
|
struct st_susfs_sus_proc_fd_link_list *cursor, *temp;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_PROC_FD_LINK, list) {
|
|
if (unlikely(!strcmp(pathname, cursor->info.target_link_name))) {
|
|
SUSFS_LOGI("[uid:%u] spoofing fd link: '%s' -> '%s'\n", current_uid().val, pathname, cursor->info.spoofed_link_name);
|
|
memset(pathname, 0, len);
|
|
strcpy(pathname, cursor->info.spoofed_link_name);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int susfs_is_sus_proc_fd_link_list_empty(void) {
|
|
return list_empty(&LH_SUS_PROC_FD_LINK);
|
|
}
|
|
|
|
int susfs_sus_memfd(char *memfd_name) {
|
|
struct st_susfs_sus_memfd_list *cursor, *temp;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_SUS_MEMFD, list) {
|
|
if (unlikely(!strcmp(memfd_name, cursor->info.target_pathname))) {
|
|
SUSFS_LOGI("prevent memfd_name: '%s' from being created\n", memfd_name);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void umount_mnt(struct path *path, int flags) {
|
|
int err = path_umount(path, flags);
|
|
|
|
/*
|
|
* path_umount() consumes the path references, so do not
|
|
* dereference path after it returns.
|
|
*/
|
|
if (err) {
|
|
SUSFS_LOGI("umount failed: %d\n", err);
|
|
}
|
|
}
|
|
|
|
static bool should_umount(struct path *path)
|
|
{
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
|
|
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
|
SUSFS_LOGI("ignore global mnt namespace process: %d\n",
|
|
current_uid().val);
|
|
return false;
|
|
}
|
|
|
|
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
|
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
|
return strcmp(fstype, "overlay") == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void try_umount(const char *mnt, bool check_mnt, int flags) {
|
|
struct path path;
|
|
int err = kern_path(mnt, 0, &path);
|
|
|
|
if (err) {
|
|
return;
|
|
}
|
|
|
|
if (path.dentry != path.mnt->mnt_root) {
|
|
// it is not root mountpoint, maybe umounted by others already.
|
|
path_put(&path);
|
|
return;
|
|
}
|
|
|
|
// we are only interest in some specific mounts
|
|
if (check_mnt && !should_umount(&path)) {
|
|
path_put(&path);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Successful handoff: path_umount() consumes these references.
|
|
*/
|
|
umount_mnt(&path, flags);
|
|
}
|
|
|
|
void susfs_try_umount(uid_t target_uid) {
|
|
struct st_susfs_try_umount_list *cursor, *temp;
|
|
|
|
list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) {
|
|
SUSFS_LOGI("umounting '%s' for uid: %d\n", cursor->info.target_pathname, target_uid);
|
|
if (cursor->info.mnt_mode == 0) {
|
|
try_umount(cursor->info.target_pathname, false, 0);
|
|
} else if (cursor->info.mnt_mode == 1) {
|
|
try_umount(cursor->info.target_pathname, false, MNT_DETACH);
|
|
}
|
|
}
|
|
}
|
|
|
|
void susfs_spoof_uname(struct new_utsname* tmp) {
|
|
if (strcmp(my_uname.sysname, "default")) {
|
|
memset(tmp->sysname, 0, __NEW_UTS_LEN);
|
|
strncpy(tmp->sysname, my_uname.sysname, __NEW_UTS_LEN);
|
|
}
|
|
if (strcmp(my_uname.nodename, "default")) {
|
|
memset(tmp->nodename, 0, __NEW_UTS_LEN);
|
|
strncpy(tmp->nodename, my_uname.nodename, __NEW_UTS_LEN);
|
|
}
|
|
if (likely(strcmp(my_uname.release, "default"))) {
|
|
memset(tmp->release, 0, __NEW_UTS_LEN);
|
|
strncpy(tmp->release, my_uname.release, __NEW_UTS_LEN);
|
|
}
|
|
if (likely(strcmp(my_uname.version, "default"))) {
|
|
memset(tmp->version, 0, __NEW_UTS_LEN);
|
|
strncpy(tmp->version, my_uname.version, __NEW_UTS_LEN);
|
|
}
|
|
if (strcmp(my_uname.machine, "default")) {
|
|
memset(tmp->machine, 0, __NEW_UTS_LEN);
|
|
strncpy(tmp->machine, my_uname.machine, __NEW_UTS_LEN);
|
|
}
|
|
}
|
|
|
|
void susfs_set_log(bool enabled) {
|
|
spin_lock(&susfs_spin_lock);
|
|
is_log_enable = enabled;
|
|
spin_unlock(&susfs_spin_lock);
|
|
if (is_log_enable) {
|
|
pr_info("susfs: enable logging to kernel");
|
|
} else {
|
|
pr_info("susfs: disable logging to kernel");
|
|
}
|
|
}
|
|
|
|
/* For files/directories in /sdcard/ but not in /sdcard/Android/data/, please delete it
|
|
* by yourself
|
|
*/
|
|
void susfs_change_error_no_by_pathname(char* const pathname, int* const errno_to_be_changed, int const syscall_family) {
|
|
if (!strncmp(pathname, "/system/", 8)||
|
|
!strncmp(pathname, "/vendor/", 8)) {
|
|
switch(syscall_family) {
|
|
case SYSCALL_FAMILY_ALL_ENOENT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_OLDNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_OLDNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
//case SYSCALL_FAMILY_RENAMEAT2_NEWNAME:
|
|
// if (!strncmp(pathname, "/system/", 8)) {
|
|
// *errno_to_be_changed = -EROFS;
|
|
// } else {
|
|
// *errno_to_be_changed = -EXDEV;
|
|
// }
|
|
// return;
|
|
default:
|
|
*errno_to_be_changed = -EROFS;
|
|
return;
|
|
}
|
|
} else if (!strncmp(pathname, "/storage/emulated/0/Android/data/", 33)) {
|
|
switch(syscall_family) {
|
|
case SYSCALL_FAMILY_ALL_ENOENT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_MKNOD:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_MKDIRAT:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_RMDIR:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_UNLINKAT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_SYMLINKAT_NEWNAME:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_OLDNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_NEWNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_OLDNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_NEWNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
default:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
}
|
|
} else if (!strncmp(pathname, "/dev/", 5)) {
|
|
switch(syscall_family) {
|
|
case SYSCALL_FAMILY_ALL_ENOENT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_MKNOD:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_MKDIRAT:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_RMDIR:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_UNLINKAT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_SYMLINKAT_NEWNAME:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_OLDNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_NEWNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_OLDNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_NEWNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
default:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
}
|
|
} else if (!strncmp(pathname, "/data/", 6)) {
|
|
switch(syscall_family) {
|
|
case SYSCALL_FAMILY_ALL_ENOENT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_MKNOD:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_MKDIRAT:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_RMDIR:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_UNLINKAT:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_SYMLINKAT_NEWNAME:
|
|
*errno_to_be_changed = -EACCES;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_OLDNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_LINKAT_NEWNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_OLDNAME:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
case SYSCALL_FAMILY_RENAMEAT2_NEWNAME:
|
|
*errno_to_be_changed = -EXDEV;
|
|
return;
|
|
default:
|
|
*errno_to_be_changed = -ENOENT;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
static int susfs_get_cur_fd_counts() {
|
|
struct fdtable *files_table;
|
|
int fd_count = 0;
|
|
|
|
files_table = files_fdtable(current->files);
|
|
for (i = 0; i < files_table->max_fds; i++) {
|
|
if (files_table->fd[i] != NULL) {
|
|
fd_count++;
|
|
}
|
|
}
|
|
return fd_count;
|
|
}
|
|
*/
|
|
|
|
static void susfs_my_uname_init(void) {
|
|
memset(&my_uname, 0, sizeof(struct st_susfs_uname));
|
|
strncpy(my_uname.sysname, "default", __NEW_UTS_LEN);
|
|
strncpy(my_uname.nodename, "default", __NEW_UTS_LEN);
|
|
strncpy(my_uname.release, "default", __NEW_UTS_LEN);
|
|
strncpy(my_uname.version, "default", __NEW_UTS_LEN);
|
|
strncpy(my_uname.machine, "default", __NEW_UTS_LEN);
|
|
}
|
|
|
|
void __init susfs_init(void) {
|
|
spin_lock_init(&susfs_spin_lock);
|
|
spin_lock_init(&susfs_mnt_id_recorder_spin_lock);
|
|
susfs_my_uname_init();
|
|
}
|
|
|
|
/* No module exit is needed becuase it should never be a loadable kernel module */
|
|
//void __init susfs_exit(void)
|