1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774 |
- /*
- * Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
- /*
- * Per-File-Tagger (PFT).
- *
- * This driver tags enterprise file for encryption/decryption,
- * as part of the Per-File-Encryption (PFE) feature.
- *
- * Enterprise registered applications are identified by their UID.
- *
- * The PFT exposes character-device interface to the user-space application,
- * to handle the following commands:
- * 1. Update registered applications list
- * 2. Encryption (in-place) of a file that was created before.
- * 3. Set State - update the state.
- *
- * The PFT exposes kernel API hooks that are intercepting file operations
- * like create/open/read/write for tagging files and also for access control.
- * It utilizes the existing security framework hooks
- * that calls the selinux hooks.
- *
- * The PFT exposes kernel API to the dm-req-crypt driver to provide the info
- * if a file is tagged or not. The dm-req-crypt driver is doing the
- * actual encryption/decryptiom.
- *
- * Tagging the file:
- * 1. Non-volatile tagging on storage using file extra-attribute (xattr).
- * 2. Volatile tagging on the file's inode, for fast access.
- *
- */
- /* Uncomment the line below to enable debug messages */
- /* #define DEBUG 1 */
- #define pr_fmt(fmt) "pft [%s]: " fmt, __func__
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/cdev.h>
- #include <linux/sched.h>
- #include <linux/uaccess.h>
- #include <linux/cred.h>
- #include <linux/list.h>
- #include <linux/errno.h>
- #include <linux/printk.h>
- #include <linux/blkdev.h>
- #include <linux/elevator.h>
- #include <linux/bio.h>
- #include <linux/bitops.h>
- #include <linux/fdtable.h>
- #include <linux/selinux.h>
- #include <linux/pft.h>
- #include <linux/msm_pft.h>
- #include "objsec.h"
- /* File tagging as encrypted/non-encrypted is valid */
- #define PFT_TAG_MAGIC ((u32)(0xABC00000))
- /* File tagged as encrypted */
- #define PFT_TAG_ENCRYPTED BIT(16)
- #define PFT_TAG_MAGIC_MASK 0xFFF00000
- #define PFT_TAG_FLAGS_MASK 0x000F0000
- #define PFT_TAG_KEY_MASK 0x0000FFFF
- /* The defualt encryption key index */
- #define PFT_DEFAULT_KEY_INDEX 1
- /* The defualt key index for non-encrypted files */
- #define PFT_NO_KEY 0
- /* PFT extended attribute name */
- #define XATTR_NAME_PFE "security.pfe"
- /* PFT driver requested major number */
- #define PFT_REQUESTED_MAJOR 213
- /* PFT driver name */
- #define DEVICE_NAME "pft"
- /* Maximum registered applications */
- #define PFT_MAX_APPS 1000
- /* Maximum command size */
- #define PFT_MAX_COMMAND_SIZE (PAGE_SIZE)
- /* Current Process ID */
- #define current_pid() ((u32)(current->pid))
- static const char *pft_state_name[PFT_STATE_MAX_INDEX] = {
- "deactivated",
- "deactivating",
- "key_removed",
- "removing_key",
- "key_loaded",
- };
- /**
- * struct pft_file_info - pft file node info.
- * @file: pointer to file stucture.
- * @pid: process ID.
- * @list: next list item.
- *
- * A node in the list of the current open encrypted files.
- */
- struct pft_file_info {
- struct file *file;
- pid_t pid;
- struct list_head list;
- };
- /**
- * struct pft_device - device state structure.
- *
- * @open_count: device open count.
- * @major: device major number.
- * @state: Per-File-Encryption state.
- * @response: command response.
- * @pfm_pid: PFM process id.
- * @inplace_file: file for in-place encryption.
- * @uid_table: registered application array (UID).
- * @uid_count: number of registered applications.
- * @open_file_list: open encrypted file list.
- * @lock: lock protect list access.
- *
- * The open_count purpose is to ensure that only one user space
- * application uses this driver.
- * The open_file_list is used to close open encrypted files
- * after the key is removed from the encryption hardware.
- */
- struct pft_device {
- struct cdev cdev;
- dev_t device_no;
- struct class *driver_class;
- int open_count;
- int major;
- enum pft_state state;
- struct pft_command_response response;
- u32 pfm_pid;
- struct file *inplace_file;
- u32 *uid_table;
- u32 uid_count;
- struct list_head open_file_list;
- struct mutex lock;
- };
- /* Device Driver State */
- static struct pft_device *pft_dev;
- static struct inode *pft_bio_get_inode(struct bio *bio);
- /**
- * pft_is_ready() - driver is initialized and ready.
- *
- * Return: true if the driver is ready.
- */
- static bool pft_is_ready(void)
- {
- return (pft_dev != NULL);
- }
- /**
- * file_to_filename() - get the filename from file pointer.
- * @filp: file pointer
- *
- * it is used for debug prints.
- *
- * Return: filename string or "unknown".
- */
- static char *file_to_filename(struct file *filp)
- {
- struct dentry *dentry = NULL;
- char *filename = NULL;
- if (!filp || !filp->f_dentry)
- return "unknown";
- dentry = filp->f_dentry;
- filename = dentry->d_iname;
- return filename;
- }
- /**
- * inode_to_filename() - get the filename from inode pointer.
- * @inode: inode pointer
- *
- * it is used for debug prints.
- *
- * Return: filename string or "unknown".
- */
- static char *inode_to_filename(struct inode *inode)
- {
- struct dentry *dentry = NULL;
- char *filename = NULL;
- if (list_empty(&inode->i_dentry))
- return "unknown";
- dentry = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
- filename = dentry->d_iname;
- return filename;
- }
- /**
- * pft_set_response() - set response error code.
- *
- * @error_code: The error code to return on response.
- */
- static inline void pft_set_response(u32 error_code)
- {
- pft_dev->response.error_code = error_code;
- }
- /**
- * pft_add_file()- Add the file to the list of opened encrypted
- * files.
- * @filp: file to add.
- *
- * Return: 0 of successful operation, negative value otherwise.
- */
- static int pft_add_file(struct file *filp)
- {
- struct pft_file_info *node = NULL;
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if (!node) {
- pr_err("malloc failure\n");
- return -ENOMEM;
- }
- node->file = filp;
- INIT_LIST_HEAD(&node->list);
- mutex_lock(&pft_dev->lock);
- list_add(&node->list, &pft_dev->open_file_list);
- pr_debug("adding file %s to open list.\n", file_to_filename(filp));
- mutex_unlock(&pft_dev->lock);
- return 0;
- }
- /**
- * pft_remove_file()- Remove the given file from the list of
- * open encrypted files.
- * @filp: file to remove.
- *
- * Return: 0 on success, negative value on failure.
- */
- static int pft_remove_file(struct file *filp)
- {
- int ret = -ENOENT;
- struct pft_file_info *tmp = NULL;
- struct list_head *pos = NULL;
- struct list_head *next = NULL;
- bool found = false;
- mutex_lock(&pft_dev->lock);
- list_for_each_safe(pos, next, &pft_dev->open_file_list) {
- tmp = list_entry(pos, struct pft_file_info, list);
- if (filp == tmp->file) {
- found = true;
- break;
- }
- }
- if (found) {
- pr_debug("remove file %s. from open list.\n ",
- file_to_filename(filp));
- list_del(&tmp->list);
- kfree(tmp);
- ret = 0;
- }
- mutex_unlock(&pft_dev->lock);
- return ret;
- }
- /**
- * pft_is_current_process_registered()- Check if current process
- * is registered.
- *
- * Return: true if current process is registered.
- */
- static bool pft_is_current_process_registered(void)
- {
- int is_registered = false;
- int i;
- u32 uid = current_uid();
- mutex_lock(&pft_dev->lock);
- for (i = 0; i < pft_dev->uid_count; i++) {
- if (pft_dev->uid_table[i] == uid) {
- pr_debug("current UID [%u] is registerd.\n", uid);
- is_registered = true;
- break;
- }
- }
- mutex_unlock(&pft_dev->lock);
- return is_registered;
- }
- /**
- * pft_is_xattr_supported() - Check if the filesystem supports
- * extended attributes.
- * @indoe: pointer to the file inode
- *
- * Return: true if supported, false if not.
- */
- static bool pft_is_xattr_supported(struct inode *inode)
- {
- if (inode == NULL) {
- pr_err("invalid argument inode passed as NULL");
- return false;
- }
- if (inode->i_security == NULL) {
- pr_debug("i_security is NULL, not ready yet\n");
- return false;
- }
- if (inode->i_op == NULL) {
- pr_debug("i_op is NULL\n");
- return false;
- }
- if (inode->i_op->getxattr == NULL) {
- pr_debug_once("getxattr() not supported , filename=%s\n",
- inode_to_filename(inode));
- return false;
- }
- if (inode->i_op->setxattr == NULL) {
- pr_debug("setxattr() not supported\n");
- return false;
- }
- return true;
- }
- /**
- * pft_get_inode_tag() - get the file tag.
- * @indoe: pointer to the file inode
- *
- * Return: tag
- */
- static u32 pft_get_inode_tag(struct inode *inode)
- {
- struct inode_security_struct *isec = inode->i_security;
- if (isec == NULL)
- return 0;
- return isec->tag;
- }
- /**
- * pft_get_inode_key_index() - get the file key.
- * @indoe: pointer to the file inode
- *
- * Return: key index
- */
- static inline u32 pft_get_inode_key_index(struct inode *inode)
- {
- return pft_get_inode_tag(inode) & PFT_TAG_KEY_MASK;
- }
- /**
- * pft_is_tag_valid() - is the tag valid
- * @indoe: pointer to the file inode
- *
- * The tagging is set to valid when an enterprise file is created
- * or when an file is opened first time after power up and the
- * xattr was checked to see if the file is encrypted or not.
- *
- * Return: true if the tag is valid.
- */
- static inline bool pft_is_tag_valid(struct inode *inode)
- {
- struct inode_security_struct *isec = inode->i_security;
- if (isec == NULL)
- return false;
- return ((isec->tag & PFT_TAG_MAGIC_MASK) == PFT_TAG_MAGIC) ?
- true : false;
- }
- /**
- * pft_is_file_encrypted() - is inode tagged as encrypted.
- *
- * @tag: holds the key index and tagging flags.
- *
- * Return: true if the file is encrypted.
- */
- static inline bool pft_is_file_encrypted(u32 tag)
- {
- return (tag & PFT_TAG_ENCRYPTED) ? true : false;
- }
- /**
- * pft_tag_inode_non_encrypted() - Tag the inode as
- * non-encrypted.
- * @indoe: pointer to the file inode
- *
- * Tag file as non-encrypted, only the valid bit is set,
- * the encrypted bit is not set.
- */
- static inline void pft_tag_inode_non_encrypted(struct inode *inode)
- {
- struct inode_security_struct *isec = inode->i_security;
- isec->tag = (u32)(PFT_TAG_MAGIC);
- }
- /**
- * pft_tag_inode_encrypted() - Tag the inode as encrypted.
- * @indoe: pointer to the file inode
- *
- * Set the valid bit, the encrypted bit, and the key index.
- */
- static void pft_tag_inode_encrypted(struct inode *inode, u32 key_index)
- {
- struct inode_security_struct *isec = inode->i_security;
- isec->tag = key_index | PFT_TAG_ENCRYPTED | PFT_TAG_MAGIC;
- }
- /**
- * pft_get_file_tag()- get the file tag.
- * @dentry: pointer to file dentry.
- * @tag_ptr: pointer to tag.
- *
- * This is the major function for detecting tag files.
- * Get the tag from the inode if tag is valid,
- * or from the xattr if this is the 1st time after power up.
- *
- * Return: 0 on successe, negative value on failure.
- */
- static int pft_get_file_tag(struct dentry *dentry, u32 *tag_ptr)
- {
- ssize_t size = 0;
- struct inode *inode;
- const char *xattr_name = XATTR_NAME_PFE;
- u32 key;
- if (!dentry || !dentry->d_inode || !tag_ptr) {
- pr_err("invalid param");
- return -EINVAL;
- }
- inode = dentry->d_inode;
- if (pft_is_tag_valid(inode)) {
- *tag_ptr = pft_get_inode_tag(inode);
- return 0;
- }
- /*
- * For the first time reading the tag, the tag is not valid, hence
- * get xattr.
- */
- size = inode->i_op->getxattr(dentry, xattr_name, &key, sizeof(key));
- if (size == -ENODATA || size == -EOPNOTSUPP) {
- pft_tag_inode_non_encrypted(inode);
- *tag_ptr = pft_get_inode_tag(inode);
- } else if (size > 0) {
- pr_debug("First time file %s opened, found xattr = %u.\n",
- inode_to_filename(inode), key);
- pft_tag_inode_encrypted(inode, key);
- *tag_ptr = pft_get_inode_tag(inode);
- } else {
- pr_err("getxattr() failure, ret=%d.\n", size);
- return -EINVAL;
- }
- return 0;
- }
- /**
- * pft_tag_file() - Tag the file saving the key_index.
- * @dentry: file dentry.
- * @key_index: encryption key index.
- *
- * This is the major fuction for tagging a file.
- * Tag the file on both the xattr and the inode.
- *
- * Return: 0 on successe, negative value on failure.
- */
- static int pft_tag_file(struct dentry *dentry, u32 key_index)
- {
- int size = 0;
- const char *xattr_name = XATTR_NAME_PFE;
- if (!dentry || !dentry->d_inode) {
- pr_err("invalid NULL param");
- return -EINVAL;
- }
- if (!pft_is_xattr_supported(dentry->d_inode)) {
- pr_err("set xattr for file %s is not support.\n",
- dentry->d_iname);
- return -EINVAL;
- }
- size = dentry->d_inode->i_op->setxattr(dentry, xattr_name, &key_index,
- sizeof(key_index), 0);
- if (size < 0) {
- pr_err("failed to set xattr for file %s, ret =%d.\n",
- dentry->d_iname, size);
- return -EFAULT;
- }
- pft_tag_inode_encrypted(dentry->d_inode, key_index);
- pr_debug("file %s tagged encrypted\n", dentry->d_iname);
- return 0;
- }
- /**
- * pft_get_app_key_index() - get the application key index.
- * @uid: registered application UID
- *
- * Get key index based on the given registered application UID.
- * Currently only one key is supported.
- *
- * Return: encryption key index.
- */
- static inline u32 pft_get_app_key_index(u32 uid)
- {
- return PFT_DEFAULT_KEY_INDEX;
- }
- /**
- * pft_is_encrypted_file() - is the file encrypted.
- * @dentry: file pointer.
- *
- * Return: true if the file is encrypted, false otherwise.
- */
- static bool pft_is_encrypted_file(struct dentry *dentry)
- {
- int rc;
- u32 tag;
- if (!pft_is_ready())
- return false;
- if (!pft_is_xattr_supported(dentry->d_inode))
- return false;
- rc = pft_get_file_tag(dentry, &tag);
- if (rc < 0)
- return false;
- return pft_is_file_encrypted(tag);
- }
- /**
- * pft_is_inplace_inode() - is this the inode of file for
- * in-place encryption.
- * @inode: inode of file to check.
- *
- * Return: true if this file is being encrypted, false
- * otherwise.
- */
- static bool pft_is_inplace_inode(struct inode *inode)
- {
- if (!pft_dev->inplace_file || !pft_dev->inplace_file->f_path.dentry)
- return false;
- return (pft_dev->inplace_file->f_path.dentry->d_inode == inode);
- }
- /**
- * pft_is_inplace_file() - is this the file for in-place
- * encryption.
- * @filp: file to check.
- *
- * A file struct might be allocated per process, inode should be
- * only one.
- *
- * Return: true if this file is being encrypted, false
- * otherwise.
- */
- static inline bool pft_is_inplace_file(struct file *filp)
- {
- if (!filp || !filp->f_path.dentry || !filp->f_path.dentry->d_inode)
- return false;
- return pft_is_inplace_inode(filp->f_path.dentry->d_inode);
- }
- /**
- * pft_get_key_index() - get the key index and other indications
- * @inode: Pointer to inode struct
- * @key_index: Pointer to the return value of key index
- * @is_encrypted: Pointer to the return value.
- * @is_inplace: Pointer to the return value.
- *
- * Provides the given inode's encryption key index, and well as
- * indications whether the file is encrypted or is it currently
- * being in-placed encrypted.
- * This API is called by the dm-req-crypt to decide if to
- * encrypt/decrypt the file.
- * File tagging depends on the hooks to be called from selinux,
- * so if selinux is disabled then tagging is also not
- * valid.
- *
- * Return: 0 on successe, negative value on failure.
- */
- int pft_get_key_index(struct bio *bio, u32 *key_index,
- bool *is_encrypted, bool *is_inplace)
- {
- u32 tag = 0;
- struct inode *inode = NULL;
- if (!pft_is_ready())
- return -ENODEV;
- if (!selinux_is_enabled())
- return -ENODEV;
- if (!bio)
- return -EPERM;
- if (!is_encrypted) {
- pr_err("is_encrypted is NULL\n");
- return -EPERM;
- }
- if (!is_inplace) {
- pr_err("is_inplace is NULL\n");
- return -EPERM;
- }
- if (!key_index) {
- pr_err("key_index is NULL\n");
- return -EPERM;
- }
- inode = pft_bio_get_inode(bio);
- if (!inode)
- return -EINVAL;
- if (!pft_is_tag_valid(inode)) {
- pr_debug("file %s, Tag not valid\n", inode_to_filename(inode));
- return -EINVAL;
- }
- if (!pft_is_xattr_supported(inode)) {
- *is_encrypted = false;
- *is_inplace = false;
- *key_index = 0;
- return 0;
- }
- tag = pft_get_inode_tag(inode);
- *is_encrypted = pft_is_file_encrypted(tag);
- *key_index = pft_get_inode_key_index(inode);
- *is_inplace = pft_is_inplace_inode(inode);
- if (*is_encrypted)
- pr_debug("file %s is encrypted\n", inode_to_filename(inode));
- return 0;
- }
- EXPORT_SYMBOL(pft_get_key_index);
- /**
- * pft_bio_get_inode() - get the inode from a bio.
- * @bio: Pointer to BIO structure.
- *
- * Walk the bio struct links to get the inode.
- *
- * Return: pointer to the inode struct if successful, or NULL otherwise.
- */
- static struct inode *pft_bio_get_inode(struct bio *bio)
- {
- if (!bio)
- return NULL;
- if (!bio->bi_io_vec)
- return NULL;
- if (!bio->bi_io_vec->bv_page)
- return NULL;
- if (PageAnon(bio->bi_io_vec->bv_page)) {
- struct inode *inode;
- /* Using direct-io (O_DIRECT) without page cache */
- inode = dio_bio_get_inode(bio);
- pr_debug("inode on direct-io, inode = 0x%x.\n", (int) inode);
- return inode;
- }
- if (!bio->bi_io_vec->bv_page->mapping)
- return NULL;
- if (!bio->bi_io_vec->bv_page->mapping->host)
- return NULL;
- return bio->bi_io_vec->bv_page->mapping->host;
- }
- /**
- * pft_allow_merge_bio()- Check if 2 BIOs can be merged.
- * @bio1: Pointer to first BIO structure.
- * @bio2: Pointer to second BIO structure.
- *
- * Prevent merging of BIOs from encrypted and non-encrypted
- * files, or files encrypted with different key.
- * This API is called by the file system block layer.
- *
- * Return: true if the BIOs allowed to be merged, false
- * otherwise.
- */
- bool pft_allow_merge_bio(struct bio *bio1, struct bio *bio2)
- {
- u32 key_index1 = 0, key_index2 = 0;
- bool is_encrypted1 = false, is_encrypted2 = false;
- bool allow = false;
- bool is_inplace = false; /* N.A. */
- int ret;
- if (!pft_is_ready())
- return true;
- ret = pft_get_key_index(bio1, &key_index1,
- &is_encrypted1, &is_inplace);
- if (ret)
- is_encrypted1 = false;
- ret = pft_get_key_index(bio2, &key_index2,
- &is_encrypted2, &is_inplace);
- if (ret)
- is_encrypted2 = false;
- allow = ((is_encrypted1 == is_encrypted2) &&
- (key_index1 == key_index2));
- return allow;
- }
- EXPORT_SYMBOL(pft_allow_merge_bio);
- /**
- * pft_inode_create() - file creation callback.
- * @dir: directory inode pointer
- * @dentry: file dentry pointer
- * @mode: flags
- *
- * This hook is called when file is created by VFS.
- * This hook is called from the selinux driver.
- * This hooks check file creation permission for enterprise
- * applications.
- * Call path:
- * vfs_create()->security_inode_create()->selinux_inode_create()
- *
- * Return: 0 on successe, negative value on failure.
- */
- int pft_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
- {
- if (!dir || !dentry)
- return 0;
- if (!pft_is_ready())
- return 0;
- switch (pft_dev->state) {
- case PFT_STATE_DEACTIVATED:
- case PFT_STATE_KEY_LOADED:
- break;
- case PFT_STATE_KEY_REMOVED:
- case PFT_STATE_DEACTIVATING:
- case PFT_STATE_REMOVING_KEY:
- /* At this state no new encrypted files can be created */
- if (pft_is_current_process_registered()) {
- pr_debug("key removed, registered uid %u is denied from creating new file %s\n",
- current_uid(), dentry->d_iname);
- return -EACCES;
- }
- break;
- default:
- BUG(); /* State is set by "set state" command */
- break;
- }
- return 0;
- }
- EXPORT_SYMBOL(pft_inode_create);
- /**
- * pft_inode_post_create() - file creation callback.
- * @dir: directory inode pointer
- * @dentry: file dentry pointer
- * @mode: flags
- *
- * This hook is called when file is created by VFS.
- * This hook is called from the selinux driver.
- * This hooks tags new files as encrypted when created by
- * enterprise applications.
- * Call path:
- * vfs_create()->security_inode_post_create()->selinux_inode_post_create()
- *
- * Return: 0 on successe, negative value on failure.
- */
- int pft_inode_post_create(struct inode *dir, struct dentry *dentry,
- umode_t mode)
- {
- int ret;
- if (!dir || !dentry)
- return 0;
- if (!pft_is_ready())
- return 0;
- switch (pft_dev->state) {
- case PFT_STATE_DEACTIVATED:
- case PFT_STATE_KEY_REMOVED:
- case PFT_STATE_DEACTIVATING:
- case PFT_STATE_REMOVING_KEY:
- break;
- case PFT_STATE_KEY_LOADED:
- /* Check whether the new file should be encrypted */
- if (pft_is_current_process_registered()) {
- u32 key_index = pft_get_app_key_index(current_uid());
- ret = pft_tag_file(dentry, key_index);
- if (ret == 0)
- pr_debug("key loaded, pid [%u] uid [%d] is creating file %s\n",
- current_pid(), current_uid(),
- dentry->d_iname);
- else {
- pr_err("Failed to tag file %s by pid %d\n",
- dentry->d_iname, current_pid());
- return -EFAULT;
- }
- }
- break;
- default:
- BUG(); /* State is set by "set state" command */
- break;
- }
- return 0;
- }
- EXPORT_SYMBOL(pft_inode_post_create);
- /**
- * pft_inode_mknod() - mknode file hook (callback)
- * @dir: directory inode pointer
- * @dentry: file dentry pointer
- * @mode: flags
- * @dev:
- *
- * This hook checks encrypted file access permission by
- * enterprise application.
- * Call path:
- * vfs_mknod()->security_inode_mknod()->selinux_inode_mknod()->pft_inode_mknod()
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- int pft_inode_mknod(struct inode *dir, struct dentry *dentry,
- umode_t mode, dev_t dev)
- {
- int rc;
- /* Check if allowed to create new encrypted files */
- rc = pft_inode_create(dir, dentry, mode);
- return rc;
- }
- EXPORT_SYMBOL(pft_inode_mknod);
- /**
- * pft_inode_rename() - file rename hook.
- * @inode: directory inode
- * @dentry: file dentry
- * @new_inode
- * @new_dentry
- *
- * Block attempt to rename enterprise file.
- *
- * Return: 0 on allowed operation, negative value otherwise.
- */
- int pft_inode_rename(struct inode *inode, struct dentry *dentry,
- struct inode *new_inode, struct dentry *new_dentry)
- {
- if (!inode || !dentry || !new_inode || !new_dentry || !dentry->d_inode)
- return 0;
- if (!pft_is_ready())
- return 0;
- /* do nothing for non-encrypted files */
- if (!pft_is_encrypted_file(dentry))
- return 0;
- pr_debug("attempt to rename encrypted file [%s]\n", dentry->d_iname);
- if (pft_is_inplace_inode(dentry->d_inode)) {
- pr_err("access in-place-encryption file %s by uid [%d] pid [%d] is blocked.\n",
- inode_to_filename(inode), current_uid(), current_pid());
- return -EACCES;
- }
- if (!pft_is_current_process_registered()) {
- pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
- current_uid(), current_pid(), dentry->d_iname);
- return -EACCES;
- } else
- pr_debug("rename file %s\n", dentry->d_iname);
- return 0;
- }
- EXPORT_SYMBOL(pft_inode_rename);
- /**
- * pft_file_open() - file open hook (callback).
- * @filp: file pointer
- * @cred: credentials pointer
- *
- * This hook is called when file is opened by VFS.
- * It is called from the selinux driver.
- * It checks enterprise file xattr when first opened.
- * It adds encrypted file to the list of open files.
- * Call path:
- * do_filp_open()->security_dentry_open()->selinux_dentry_open()
- *
- * Return: 0 on successe, negative value on failure.
- */
- int pft_file_open(struct file *filp, const struct cred *cred)
- {
- int ret;
- if (!filp || !filp->f_path.dentry)
- return 0;
- if (!pft_is_ready())
- return 0;
- if (filp->f_flags & O_DIRECT)
- pr_debug("file %s using O_DIRECT.\n", file_to_filename(filp));
- /* do nothing for non-encrypted files */
- if (!pft_is_encrypted_file(filp->f_dentry))
- return 0;
- /*
- * Only PFM allowed to access in-place-encryption-file
- * during in-place-encryption process
- */
- if (pft_is_inplace_file(filp) && current_pid() != pft_dev->pfm_pid) {
- pr_err("Access in-place-encryption file %s by uid %d pid %d is blocked.\n",
- file_to_filename(filp), current_uid(), current_pid());
- return -EACCES;
- }
- switch (pft_dev->state) {
- case PFT_STATE_DEACTIVATED:
- case PFT_STATE_KEY_REMOVED:
- case PFT_STATE_DEACTIVATING:
- case PFT_STATE_REMOVING_KEY:
- /* Block any access for encrypted files when key not loaded */
- pr_debug("key not loaded. uid (%u) can not access file %s\n",
- current_uid(), file_to_filename(filp));
- return -EACCES;
- case PFT_STATE_KEY_LOADED:
- /* Only registered apps may access encrypted files. */
- if (!pft_is_current_process_registered()) {
- pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
- current_uid(), current_pid(),
- file_to_filename(filp));
- return -EACCES;
- }
- ret = pft_add_file(filp);
- if (ret) {
- pr_err("failed to add file %s to the list.\n",
- file_to_filename(filp));
- return -EFAULT;
- }
- break;
- default:
- BUG(); /* State is set by "set state" command */
- break;
- }
- return 0;
- }
- EXPORT_SYMBOL(pft_file_open);
- /**
- * pft_file_permission() - check file access permission.
- * @filp: file pointer
- * @mask: flags
- *
- * This hook is called when file is read/write by VFS.
- * This hook is called from the selinux driver.
- * This hook checks encrypted file access permission by
- * enterprise application.
- * Call path:
- * vfs_read()->security_file_permission()->selinux_file_permission()
- *
- * Return: 0 on successe, negative value on failure.
- */
- int pft_file_permission(struct file *filp, int mask)
- {
- if (!filp)
- return 0;
- if (!pft_is_ready())
- return 0;
- /* do nothing for non-encrypted files */
- if (!pft_is_encrypted_file(filp->f_dentry))
- return 0;
- /*
- * Only PFM allowed to access in-place-encryption-file
- * during in-place encryption process
- */
- if (pft_is_inplace_file(filp)) {
- if (current_pid() == pft_dev->pfm_pid) {
- /* mask MAY_WRITE=2 / MAY_READ=4 */
- pr_debug("r/w [mask 0x%x] in-place-encryption file %s by PFM (UID %d, PID %d).\n",
- mask, file_to_filename(filp),
- current_uid(), current_pid());
- return 0;
- } else {
- pr_err("Access in-place-encryption file %s by App (UID %d, PID %d) is blocked.\n",
- file_to_filename(filp),
- current_uid(), current_pid());
- return -EACCES;
- }
- }
- switch (pft_dev->state) {
- case PFT_STATE_DEACTIVATED:
- case PFT_STATE_KEY_REMOVED:
- case PFT_STATE_DEACTIVATING:
- case PFT_STATE_REMOVING_KEY:
- /* Block any access for encrypted files when key not loaded */
- pr_debug("key not loaded. uid (%u) can not access file %s\n",
- current_uid(), file_to_filename(filp));
- return -EACCES;
- case PFT_STATE_KEY_LOADED:
- /* Only registered apps can access encrypted files. */
- if (!pft_is_current_process_registered()) {
- pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
- current_uid(), current_pid(),
- file_to_filename(filp));
- return -EACCES;
- }
- break;
- default:
- BUG(); /* State is set by "set state" command */
- break;
- }
- return 0;
- }
- EXPORT_SYMBOL(pft_file_permission);
- /**
- * pft_sync_file() - sync the file.
- * @filp: file pointer
- *
- * Complete writting any pending write request of encrypted data
- * before key is removed, to avoid writting garbage to
- * enterprise files.
- */
- static void pft_sync_file(struct file *filp)
- {
- int ret;
- ret = vfs_fsync(filp, false);
- if (ret)
- pr_debug("failed to sync file %s, ret = %d.\n",
- file_to_filename(filp), ret);
- else
- pr_debug("Sync file %s ok.\n", file_to_filename(filp));
- }
- /**
- * pft_file_close()- handle file close event
- * @filp: file pointer
- *
- * This hook is called when file is closed by VFS.
- * This hook is called from the selinux driver.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- int pft_file_close(struct file *filp)
- {
- if (!filp)
- return 0;
- if (!pft_is_ready())
- return 0;
- /* do nothing for non-encrypted files */
- if (!pft_is_encrypted_file(filp->f_dentry))
- return 0;
- if (pft_is_inplace_file(filp)) {
- pr_debug("pid [%u] uid [%u] is closing in-place-encryption file %s\n",
- current_pid(), current_uid(), file_to_filename(filp));
- pft_dev->inplace_file = NULL;
- }
- pft_sync_file(filp);
- pft_remove_file(filp);
- return 0;
- }
- EXPORT_SYMBOL(pft_file_close);
- /**
- * pft_inode_unlink() - Delete file hook.
- * @dir: directory inode pointer
- * @dentry: file dentry pointer
- *
- * call path: vfs_unlink()->security_inode_unlink().
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- int pft_inode_unlink(struct inode *dir, struct dentry *dentry)
- {
- struct inode *inode = NULL;
- if (!dir || !dentry || !dentry->d_inode)
- return 0;
- if (!pft_is_ready())
- return 0;
- inode = dentry->d_inode;
- /* do nothing for non-encrypted files */
- if (!pft_is_encrypted_file(dentry))
- return 0;
- if (pft_is_inplace_inode(inode)) {
- pr_err("block delete in-place-encryption file %s by uid [%d] pid [%d], while encryption in progress.\n",
- inode_to_filename(inode), current_uid(), current_pid());
- return -EBUSY;
- }
- if (!pft_is_current_process_registered()) {
- pr_err("unregistered app (uid %u pid %u) is trying to access encrypted file %s\n",
- current_uid(), current_pid(), inode_to_filename(inode));
- return -EACCES;
- } else
- pr_debug("delete file %s\n", inode_to_filename(inode));
- return 0;
- }
- EXPORT_SYMBOL(pft_inode_unlink);
- /**
- * pft_inode_set_xattr() - set/remove xattr callback.
- * @dentry: file dentry pointer
- * @name: xattr name.
- *
- * This hook checks attempt to set/remove PFE xattr.
- * Only this kernel driver allows to set the PFE xattr, so block
- * any attempt to do it from user space. Allow access for other
- * xattr.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- int pft_inode_set_xattr(struct dentry *dentry, const char *name)
- {
- struct inode *inode = NULL;
- if (!dentry || !dentry->d_inode)
- return 0;
- inode = dentry->d_inode;
- if (strcmp(name, XATTR_NAME_PFE) != 0) {
- pr_debug("xattr name=%s file %s\n", name,
- inode_to_filename(inode));
- return 0; /* Not PFE xattr so it is ok */
- }
- pr_err("Attemp to set/remove PFE xattr for file %s\n",
- inode_to_filename(inode));
- /* Only PFT kernel driver allows to set the PFE xattr */
- return -EACCES;
- }
- EXPORT_SYMBOL(pft_inode_set_xattr);
- /**
- * pft_close_opened_enc_files() - Close all the currently open
- * encrypted files
- *
- * Close all open encrypted file when removing key or
- * deactivating.
- */
- static void pft_close_opened_enc_files(void)
- {
- struct pft_file_info *tmp = NULL;
- struct list_head *pos = NULL;
- struct list_head *next = NULL;
- list_for_each_safe(pos, next, &pft_dev->open_file_list) {
- struct file *filp;
- tmp = list_entry(pos, struct pft_file_info, list);
- filp = tmp->file;
- pr_debug("closing file %s.\n", file_to_filename(filp));
- /* filp_close() eventually calls pft_file_close() */
- filp_close(filp, NULL);
- }
- }
- /**
- * pft_set_state() - Handle "Set State" command
- * @command: command buffer.
- * @size: size of command buffer.
- *
- * The command execution status is reported by the response.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- static int pft_set_state(struct pft_command *command, int size)
- {
- u32 state = command->set_state.state;
- int expected_size = sizeof(command->opcode) +
- sizeof(command->set_state);
- if (size != expected_size) {
- pr_err("Invalid buffer size\n");
- pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
- return -EINVAL;
- }
- if (state >= PFT_STATE_MAX_INDEX) {
- pr_err("Invalid state %d\n", command->set_state.state);
- pft_set_response(PFT_CMD_RESP_INVALID_STATE);
- return 0;
- }
- pr_debug("Set State %d [%s].\n", state, pft_state_name[state]);
- switch (command->set_state.state) {
- case PFT_STATE_DEACTIVATING:
- case PFT_STATE_REMOVING_KEY:
- pft_close_opened_enc_files();
- /* Fall through */
- case PFT_STATE_DEACTIVATED:
- case PFT_STATE_KEY_LOADED:
- case PFT_STATE_KEY_REMOVED:
- pft_dev->state = command->set_state.state;
- pft_set_response(PFT_CMD_RESP_SUCCESS);
- break;
- default:
- pr_err("Invalid state %d\n", command->set_state.state);
- pft_set_response(PFT_CMD_RESP_INVALID_STATE);
- break;
- }
- return 0;
- }
- /**
- * pft_get_process_open_file() - get file pointer using file
- * descriptor index.
- * @index: file descriptor index.
- *
- * Return: file pointer on success, NULL on failure.
- */
- static struct file *pft_get_process_open_file(int index)
- {
- struct fdtable *files_table;
- files_table = files_fdtable(current->files);
- if (files_table == NULL)
- return NULL;
- if (index >= files_table->max_fds)
- return NULL;
- else
- return files_table->fd[index];
- }
- /**
- * pft_set_inplace_file() - handle "inplace file encryption"
- * command.
- * @command: command buffer.
- * @size: size of command buffer.
- *
- * The command execution status is reported by the response.
- *
- * Return: 0 if command is valid, negative value otherwise.
- */
- static int pft_set_inplace_file(struct pft_command *command, int size)
- {
- int expected_size;
- u32 fd;
- int rc;
- struct file *filp = NULL;
- struct inode *inode = NULL;
- int writecount;
- expected_size = sizeof(command->opcode) +
- sizeof(command->preform_in_place_file_enc.file_descriptor);
- if (size != expected_size) {
- pr_err("invalid command size %d expected %d.\n",
- size, expected_size);
- pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
- return -EINVAL;
- }
- if (pft_dev->state != (u32) PFT_STATE_KEY_LOADED) {
- pr_err("Key not loaded, state [%d], In-place-encryption is not allowed.\n",
- pft_dev->state);
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- return 0;
- }
- /* allow only one in-place file encryption at a time */
- if (pft_dev->inplace_file != NULL) {
- pr_err("file %s in-place-encryption in progress.\n",
- file_to_filename(pft_dev->inplace_file));
- /* @todo - use new error code */
- pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
- return 0;
- }
- fd = command->preform_in_place_file_enc.file_descriptor;
- filp = pft_get_process_open_file(fd);
- if (filp == NULL) {
- pr_err("failed to find file by fd %d.\n", fd);
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- return 0;
- }
- /* Verify the file is not already open by other than PFM */
- if (!filp->f_path.dentry || !filp->f_path.dentry->d_inode) {
- pr_err("failed to get inode of inplace-file.\n");
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- return 0;
- }
- inode = filp->f_path.dentry->d_inode;
- writecount = atomic_read(&inode->i_writecount);
- if (writecount > 1) {
- pr_err("file %s is opened %d times for write.\n",
- file_to_filename(filp), writecount);
- pft_set_response(PFT_CMD_RESP_INPLACE_FILE_IS_OPEN);
- return 0;
- }
- /*
- * Check if the file was already encryprted.
- * In practice, it is unlikely to happen,
- * because PFM is not an enterprise application
- * it won't be able to open encrypted file.
- */
- if (pft_is_encrypted_file(filp->f_dentry)) {
- pr_err("file %s is already encrypted.\n",
- file_to_filename(filp));
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- return 0;
- }
- /* Update the current in-place-encryption file */
- pft_dev->inplace_file = filp;
- /*
- * Now, any new access to this file is allowed only to PFM.
- * Lets make sure that all pending writes are completed
- * before encrypting the file.
- */
- pft_sync_file(filp);
- rc = pft_tag_file(pft_dev->inplace_file->f_dentry,
- pft_get_app_key_index(current_uid()));
- if (!rc) {
- pr_debug("tagged file %s to be encrypted.\n",
- file_to_filename(pft_dev->inplace_file));
- pft_set_response(PFT_CMD_RESP_SUCCESS);
- } else {
- pr_err("failed to tag file %s for encryption.\n",
- file_to_filename(pft_dev->inplace_file));
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- }
- return 0;
- }
- /**
- * pft_update_reg_apps() - Update the registered application
- * list.
- * @command: command buffer.
- * @size: size of command buffer.
- *
- * The command execution status is reported by the response.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- static int pft_update_reg_apps(struct pft_command *command, int size)
- {
- int i;
- int expected_size;
- void *buf;
- int buf_size;
- u32 items_count = command->update_app_list.items_count;
- if (items_count > PFT_MAX_APPS) {
- pr_err("Number of apps [%d] > max apps [%d]\n",
- items_count , PFT_MAX_APPS);
- pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
- return -EINVAL;
- }
- expected_size =
- sizeof(command->opcode) +
- sizeof(command->update_app_list.items_count) +
- (command->update_app_list.items_count * sizeof(u32));
- if (size != expected_size) {
- pr_err("invalid command size %d expected %d.\n",
- size, expected_size);
- pft_set_response(PFT_CMD_RESP_INVALID_CMD_PARAMS);
- return -EINVAL;
- }
- mutex_lock(&pft_dev->lock);
- /* Free old table */
- kfree(pft_dev->uid_table);
- pft_dev->uid_table = NULL;
- pft_dev->uid_count = 0;
- if (items_count == 0) {
- pr_info("empty app list - clear list.\n");
- mutex_unlock(&pft_dev->lock);
- return 0;
- }
- buf_size = command->update_app_list.items_count * sizeof(u32);
- buf = kzalloc(buf_size, GFP_KERNEL);
- if (!buf) {
- pr_err("malloc failure\n");
- pft_set_response(PFT_CMD_RESP_GENERAL_ERROR);
- mutex_unlock(&pft_dev->lock);
- return 0;
- }
- pft_dev->uid_table = buf;
- pft_dev->uid_count = command->update_app_list.items_count;
- pr_debug("uid_count = %d\n", pft_dev->uid_count);
- for (i = 0; i < pft_dev->uid_count; i++)
- pft_dev->uid_table[i] = command->update_app_list.table[i];
- pft_set_response(PFT_CMD_RESP_SUCCESS);
- mutex_unlock(&pft_dev->lock);
- return 0;
- }
- /**
- * pft_handle_command() - Handle user space app commands.
- * @buf: command buffer.
- * @buf_size: command buffer size.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
- static int pft_handle_command(void *buf, int buf_size)
- {
- int ret = 0;
- struct pft_command *command = NULL;
- /* opcode field is the minimum length of command */
- if (buf_size < sizeof(command->opcode)) {
- pr_err("Invalid argument used buffer size\n");
- return -EINVAL;
- }
- command = (struct pft_command *)buf;
- pft_dev->response.command_id = command->opcode;
- switch (command->opcode) {
- case PFT_CMD_OPCODE_SET_STATE:
- ret = pft_set_state(command, buf_size);
- break;
- case PFT_CMD_OPCODE_UPDATE_REG_APP_UID:
- ret = pft_update_reg_apps(command, buf_size);
- break;
- case PFT_CMD_OPCODE_PERFORM_IN_PLACE_FILE_ENC:
- ret = pft_set_inplace_file(command, buf_size);
- break;
- default:
- pr_err("Invalid command_op_code %u\n", command->opcode);
- pft_set_response(PFT_CMD_RESP_INVALID_COMMAND);
- return 0;
- }
- return ret;
- }
- static int pft_device_open(struct inode *inode, struct file *file)
- {
- int ret;
- mutex_lock(&pft_dev->lock);
- if (pft_dev->open_count > 0) {
- pr_err("PFT device is already opened (%d)\n",
- pft_dev->open_count);
- ret = -EBUSY;
- } else {
- pft_dev->open_count++;
- pft_dev->pfm_pid = current_pid();
- pr_debug("PFT device opened by %d (%d)\n",
- pft_dev->pfm_pid, pft_dev->open_count);
- ret = 0;
- }
- mutex_unlock(&pft_dev->lock);
- pr_debug("device opened, count %d\n", pft_dev->open_count);
- return ret;
- }
- static int pft_device_release(struct inode *inode, struct file *file)
- {
- mutex_lock(&pft_dev->lock);
- if (pft_dev->open_count > 0)
- pft_dev->open_count--;
- pft_dev->pfm_pid = UINT_MAX;
- mutex_unlock(&pft_dev->lock);
- pr_debug("device released, count %d\n", pft_dev->open_count);
- return 0;
- }
- /**
- * pft_device_write() - Get commands from user sapce.
- *
- * Return: number of bytes to write on success to get the
- * command buffer, negative value on failure.
- * The error code for handling the command should be retrive by
- * reading the response.
- * Note: any reurn value of 0..size-1 will cause retry by the
- * OS, so avoid it.
- */
- static ssize_t pft_device_write(struct file *filp, const char __user *user_buff,
- size_t size, loff_t *f_pos)
- {
- int ret;
- char *cmd_buf;
- if (size > PFT_MAX_COMMAND_SIZE || !user_buff || !f_pos) {
- pr_err("inavlid parameters.\n");
- return -EINVAL;
- }
- cmd_buf = kzalloc(size, GFP_KERNEL);
- if (cmd_buf == NULL) {
- pr_err("malloc failure for command buffer\n");
- return -ENOMEM;
- }
- ret = copy_from_user(cmd_buf, user_buff, size);
- if (ret) {
- pr_err("Unable to copy from user (err %d)\n", ret);
- kfree(cmd_buf);
- return -EFAULT;
- }
- ret = pft_handle_command(cmd_buf, size);
- if (ret) {
- kfree(cmd_buf);
- return -EFAULT;
- }
- kfree(cmd_buf);
- return size;
- }
- /**
- * pft_device_read() - return response of last command.
- *
- * Return: number of bytes to read on success, negative value on
- * failure.
- */
- static ssize_t pft_device_read(struct file *filp, char __user *buffer,
- size_t length, loff_t *f_pos)
- {
- int ret = 0;
- if (!buffer || !f_pos || length < sizeof(pft_dev->response)) {
- pr_err("inavlid parameters.\n");
- return -EFAULT;
- }
- ret = copy_to_user(buffer, &(pft_dev->response),
- sizeof(pft_dev->response));
- if (ret) {
- pr_err("Unable to copy to user, err = %d.\n", ret);
- return -EINVAL;
- }
- return sizeof(pft_dev->response);
- }
- static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .read = pft_device_read,
- .write = pft_device_write,
- .open = pft_device_open,
- .release = pft_device_release,
- };
- static int __init pft_register_chardev(void)
- {
- int rc;
- unsigned baseminor = 0;
- unsigned count = 1;
- struct device *class_dev;
- rc = alloc_chrdev_region(&pft_dev->device_no, baseminor, count,
- DEVICE_NAME);
- if (rc < 0) {
- pr_err("alloc_chrdev_region failed %d\n", rc);
- return rc;
- }
- pft_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME);
- if (IS_ERR(pft_dev->driver_class)) {
- rc = -ENOMEM;
- pr_err("class_create failed %d\n", rc);
- goto exit_unreg_chrdev_region;
- }
- class_dev = device_create(pft_dev->driver_class, NULL,
- pft_dev->device_no, NULL,
- DEVICE_NAME);
- if (!class_dev) {
- pr_err("class_device_create failed %d\n", rc);
- rc = -ENOMEM;
- goto exit_destroy_class;
- }
- cdev_init(&pft_dev->cdev, &fops);
- pft_dev->cdev.owner = THIS_MODULE;
- rc = cdev_add(&pft_dev->cdev, MKDEV(MAJOR(pft_dev->device_no), 0), 1);
- if (rc < 0) {
- pr_err("cdev_add failed %d\n", rc);
- goto exit_destroy_device;
- }
- return 0;
- exit_destroy_device:
- device_destroy(pft_dev->driver_class, pft_dev->device_no);
- exit_destroy_class:
- class_destroy(pft_dev->driver_class);
- exit_unreg_chrdev_region:
- unregister_chrdev_region(pft_dev->device_no, 1);
- return rc;
- }
- static void __exit pft_unregister_chrdev(void)
- {
- cdev_del(&pft_dev->cdev);
- device_destroy(pft_dev->driver_class, pft_dev->device_no);
- class_destroy(pft_dev->driver_class);
- unregister_chrdev_region(pft_dev->device_no, 1);
- }
- static void __exit pft_free_open_files_list(void)
- {
- struct pft_file_info *tmp = NULL;
- struct list_head *pos = NULL;
- struct list_head *next = NULL;
- mutex_lock(&pft_dev->lock);
- list_for_each_safe(pos, next, &pft_dev->open_file_list) {
- tmp = list_entry(pos, struct pft_file_info, list);
- list_del(&tmp->list);
- kfree(tmp);
- }
- mutex_unlock(&pft_dev->lock);
- }
- static void __exit pft_exit(void)
- {
- if (pft_dev == NULL)
- return;
- pft_unregister_chrdev();
- pft_free_open_files_list();
- kfree(pft_dev->uid_table);
- kfree(pft_dev);
- pft_dev = NULL;
- }
- static int __init pft_init(void)
- {
- int ret;
- struct pft_device *dev = NULL;
- dev = kzalloc(sizeof(struct pft_device), GFP_KERNEL);
- if (dev == NULL) {
- pr_err("No memory for device structr\n");
- return -ENOMEM;
- }
- pft_dev = dev;
- dev->state = PFT_STATE_DEACTIVATED;
- dev->pfm_pid = UINT_MAX;
- INIT_LIST_HEAD(&dev->open_file_list);
- mutex_init(&dev->lock);
- ret = pft_register_chardev();
- if (ret) {
- pr_err("create character device failed.\n");
- goto fail;
- }
- pr_info("Drivr initialized successfully %s %s.n", __DATE__, __TIME__);
- return 0;
- fail:
- pr_err("Failed to init driver.\n");
- kfree(dev);
- pft_dev = NULL;
- return -ENODEV;
- }
- module_init(pft_init);
- module_exit(pft_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Per-File-Tagger driver");
|