You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
7.9 KiB
350 lines
7.9 KiB
/*
|
|
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
|
|
*
|
|
* This program 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 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/version.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/hashtable.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/module.h>
|
|
#include "mount.h"
|
|
|
|
#define DEFINE_DLOG(_name) extern int fslog_##_name(const char *fmt, ...)
|
|
DEFINE_DLOG(dlog_mm);
|
|
DEFINE_DLOG(dlog_efs);
|
|
DEFINE_DLOG(dlog_etc);
|
|
DEFINE_DLOG(dlog_rmdir);
|
|
|
|
#define EXT_SHIFT 7
|
|
#define MAX_EXT (1 << 7)
|
|
#define MAX_DEPTH 2
|
|
#define MEDIA_MIN_SIZE (512 * 1024)
|
|
|
|
struct dlog_keyword {
|
|
struct hlist_node hlist;
|
|
const char *keyword;
|
|
};
|
|
|
|
struct dlog_keyword_hash_tbl {
|
|
DECLARE_HASHTABLE(table, 7);
|
|
};
|
|
|
|
enum {
|
|
DLOG_HT_EXTENSION = 0,
|
|
DLOG_HT_EXCEPTION,
|
|
DLOG_HT_MAX
|
|
};
|
|
|
|
enum {
|
|
DLOG_SUPP_PART_DATA = 0,
|
|
DLOG_SUPP_PART_EFS,
|
|
DLOG_SUPP_PART_FUSE,
|
|
DLOG_SUPP_PART_MAX
|
|
};
|
|
|
|
enum {
|
|
DLOG_MM = 0,
|
|
DLOG_EFS,
|
|
DLOG_ETC,
|
|
DLOG_RMDIR
|
|
};
|
|
|
|
static struct dlog_keyword_hash_tbl ht[DLOG_HT_MAX];
|
|
|
|
static const char *support_part[] = {
|
|
"data", "efs", "emulated", NULL,
|
|
};
|
|
|
|
static const char *extensions[] = {
|
|
/* image */
|
|
"arw", "bmp", "cr2", "dng", "gif",
|
|
"jpeg", "jpg", "nef", "nrw", "orf",
|
|
"pef", "png", "raf", "rw2", "srw",
|
|
"wbmp", "webp",
|
|
/* audio */
|
|
"3ga", "aac", "amr",
|
|
"awb", "dff", "dsf", "flac", "imy",
|
|
"m4a", "mid", "midi", "mka", "mp3",
|
|
"mpga", "mxmf", "oga", "ogg", "ota",
|
|
"rtttl", "rtx", "smf", "wav", "wma",
|
|
"xmf", "dcf",
|
|
/* video */
|
|
"3g2", "3gp", "3gpp", "3gpp2",
|
|
"ak3g", "asf", "avi", "divx", "flv",
|
|
"m2t", "m2ts", "m4v", "mkv", "mp4",
|
|
"mpeg", "mpg", "mts", "skm", "tp",
|
|
"trp", "ts", "webm", "wmv",
|
|
/* document */
|
|
"asc",
|
|
"csv", "doc", "docm", "docx", "dot",
|
|
"dotx", "htm", "html", "hwdt", "hwp",
|
|
"hwpx", "hwt", "memo", "pdf", "pot",
|
|
"potx", "pps", "ppsx", "ppt", "pptm",
|
|
"pptx", "rtf", "sdoc", "snb", "spd",
|
|
"xls", "xlsm", "xlsx", "xlt", "xltx",
|
|
"xml",
|
|
NULL,
|
|
};
|
|
|
|
static const char *exceptions[] = {
|
|
/* exception extension */
|
|
"db-shm", "shm", "bak", "mbak", "gz",
|
|
"log", "swap", "dcs", "tmp", "temp",
|
|
"txt", NULL,
|
|
};
|
|
|
|
static const char **dlog_keyword_tbl[DLOG_HT_MAX] = {
|
|
extensions, exceptions
|
|
};
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
|
|
static inline unsigned int dlog_full_name_hash(const char *name, unsigned int len)
|
|
{
|
|
return full_name_hash((void *)0x0, name, len);
|
|
}
|
|
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) */
|
|
static inline unsigned int dlog_full_name_hash(const char *name, unsigned int len)
|
|
{
|
|
return full_name_hash(name, len);
|
|
}
|
|
#endif
|
|
|
|
bool is_ext(const char *name, const char token, struct dlog_keyword_hash_tbl *hash_tbl)
|
|
{
|
|
char *ext = strrchr(name, token);
|
|
struct dlog_keyword *hash_cur;
|
|
unsigned hash;
|
|
int i;
|
|
bool ret = false;
|
|
|
|
if (!ext || !ext[1])
|
|
return false;
|
|
|
|
ext = kstrdup(&ext[1], GFP_KERNEL);
|
|
if (!ext)
|
|
return false;
|
|
|
|
for (i = 0; ext[i]; i++)
|
|
ext[i] = tolower(ext[i]);
|
|
hash = dlog_full_name_hash(ext, strlen(ext));
|
|
|
|
hash_for_each_possible(hash_tbl->table, hash_cur, hlist, hash) {
|
|
if (!strcmp(ext, hash_cur->keyword)) {
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
kfree(ext);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int __init dlog_keyword_hash_init(void)
|
|
{
|
|
int i;
|
|
int tbl_idx = 0;
|
|
|
|
do {
|
|
hash_init(ht[tbl_idx].table);
|
|
for (i = 0; dlog_keyword_tbl[tbl_idx][i]; i++) {
|
|
struct dlog_keyword *hte;
|
|
|
|
hte = kzalloc(sizeof(struct dlog_keyword), GFP_KERNEL);
|
|
if (!hte)
|
|
return -ENOMEM;
|
|
|
|
INIT_HLIST_NODE(&hte->hlist);
|
|
hte->keyword = dlog_keyword_tbl[tbl_idx][i];
|
|
hash_add(ht[tbl_idx].table, &hte->hlist,
|
|
dlog_full_name_hash(hte->keyword, strlen(hte->keyword)));
|
|
}
|
|
tbl_idx++;
|
|
} while(tbl_idx < DLOG_HT_MAX);
|
|
|
|
return 0;
|
|
}
|
|
module_init(dlog_keyword_hash_init);
|
|
|
|
void __exit dlog_keyword_hash_exit(void)
|
|
{
|
|
int i;
|
|
int num_ht = 0;
|
|
struct dlog_keyword *hash_cur;
|
|
|
|
do {
|
|
hash_for_each(ht[num_ht].table, i, hash_cur, hlist)
|
|
kfree(hash_cur);
|
|
} while(++num_ht < DLOG_HT_MAX);
|
|
|
|
}
|
|
module_exit(dlog_keyword_hash_exit);
|
|
|
|
static int get_support_part_id(struct vfsmount *mnt)
|
|
{
|
|
int idx = 0;
|
|
struct mount *mount = real_mount(mnt);
|
|
|
|
/* check partition which need register delete log */
|
|
do {
|
|
if (!strcmp(mount->mnt_mountpoint->d_name.name, support_part[idx]))
|
|
return idx;
|
|
} while(support_part[++idx]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int is_sdcard(struct vfsmount *mnt)
|
|
{
|
|
/* internal storage (external storage till Andorid 8.x) */
|
|
if (mnt->mnt_sb->s_magic == SDCARDFS_SUPER_MAGIC)
|
|
return true;
|
|
/* external storage from Android 9.x */
|
|
else if (mnt->mnt_sb->s_magic == SDFAT_SUPER_MAGIC)
|
|
return true;
|
|
else if (mnt->mnt_sb->s_magic == MSDOS_SUPER_MAGIC)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static void make_prefix(int part_id, char *prefix)
|
|
{
|
|
if (part_id == DLOG_SUPP_PART_DATA)
|
|
*prefix = 'd';
|
|
else
|
|
*prefix = 's';
|
|
}
|
|
|
|
static void store_log(struct dentry *dentry, struct inode *inode,
|
|
struct path *path, int type, int part_id)
|
|
{
|
|
kuid_t euid = current->cred->euid;
|
|
unsigned long ino = inode ? inode->i_ino : 0;
|
|
loff_t isize = inode ? inode->i_size : 0;
|
|
char prefix = 0;
|
|
char *buf, *full_path;
|
|
char unit = 'B';
|
|
|
|
buf = kzalloc(PATH_MAX, GFP_KERNEL);
|
|
if (!buf) {
|
|
printk(KERN_ERR "%s memory alloc failed : ENOMEM\n", __func__);
|
|
return;
|
|
}
|
|
|
|
full_path = dentry_path_raw(dentry, buf, PATH_MAX);
|
|
if (IS_ERR(full_path))
|
|
goto out;
|
|
|
|
make_prefix(part_id, &prefix);
|
|
if (isize >> 10) {
|
|
isize >>= 10;
|
|
unit = 'K';
|
|
}
|
|
|
|
if (type == DLOG_MM) {
|
|
fslog_dlog_mm("[%c]\"%s\" (%u, %lu, %lu, %lld%c)\n", prefix, full_path,
|
|
euid, path->dentry->d_inode->i_ino, ino, isize, unit);
|
|
goto out;
|
|
}
|
|
|
|
if (type == DLOG_ETC) {
|
|
fslog_dlog_etc("[%c]\"%s\" (%u, %lu, %lu, %lld%c)\n", prefix, full_path,
|
|
euid, path->dentry->d_inode->i_ino, ino, isize, unit);
|
|
goto out;
|
|
}
|
|
|
|
if (type == DLOG_EFS) {
|
|
fslog_dlog_efs("\"%s\" (%lu, %lu, %lld%c)\n", full_path,
|
|
path->dentry->d_inode->i_ino, ino, isize, unit);
|
|
goto out;
|
|
}
|
|
|
|
if (type == DLOG_RMDIR)
|
|
fslog_dlog_rmdir("[%c]\"%s\" (%u, %lu)\n", prefix, full_path,
|
|
euid, path->dentry->d_inode->i_ino);
|
|
out:
|
|
kfree(buf);
|
|
}
|
|
|
|
void dlog_hook(struct dentry *dentry, struct inode *inode, struct path *path)
|
|
{
|
|
int part_id = get_support_part_id(path->mnt);
|
|
|
|
if ((part_id < 0) && !is_sdcard(path->mnt))
|
|
return;
|
|
|
|
/* for efs partition */
|
|
if (part_id == DLOG_SUPP_PART_EFS) {
|
|
store_log(dentry, inode, path, DLOG_EFS, part_id);
|
|
goto out;
|
|
}
|
|
|
|
/* for data partition`s only multimedia file */
|
|
if (is_ext(dentry->d_name.name, '.', &ht[DLOG_HT_EXTENSION])) {
|
|
if (i_size_read(inode) >= MEDIA_MIN_SIZE)
|
|
store_log(dentry, inode, path, DLOG_MM, part_id);
|
|
goto out;
|
|
}
|
|
|
|
/* for data partition except multimedia file */
|
|
if (!is_ext(dentry->d_name.name, '.', &ht[DLOG_HT_EXCEPTION])
|
|
&& !is_ext(dentry->d_name.name, '-', &ht[DLOG_HT_EXCEPTION]))
|
|
store_log(dentry, inode, path, DLOG_ETC, part_id);
|
|
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static int get_dentry_depth(struct dentry *dentry)
|
|
{
|
|
int depth = 0;
|
|
struct dentry *tmp_de = dentry;
|
|
|
|
while (!IS_ROOT(tmp_de)) {
|
|
struct dentry *parent = tmp_de->d_parent;
|
|
|
|
depth++;
|
|
if (depth > MAX_DEPTH)
|
|
return -1;
|
|
tmp_de = parent;
|
|
}
|
|
|
|
return depth;
|
|
}
|
|
|
|
void dlog_hook_rmdir(struct dentry *dentry, struct path *path)
|
|
{
|
|
int depth = 0;
|
|
int part_id = get_support_part_id(path->mnt);
|
|
|
|
if (part_id != DLOG_SUPP_PART_DATA)
|
|
return;
|
|
|
|
depth = get_dentry_depth(dentry);
|
|
if (depth < 0)
|
|
return;
|
|
|
|
store_log(dentry, NULL, path, DLOG_RMDIR, part_id);
|
|
|
|
return;
|
|
}
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Logging unlink file path");
|
|
MODULE_AUTHOR("Samsung Electronics Co., Ltd.");
|
|
|