13323eec9SMimi Zohar /* 23323eec9SMimi Zohar * Copyright (C) 2008 IBM Corporation 33323eec9SMimi Zohar * 43323eec9SMimi Zohar * Author: Mimi Zohar <zohar@us.ibm.com> 53323eec9SMimi Zohar * 63323eec9SMimi Zohar * This program is free software; you can redistribute it and/or 73323eec9SMimi Zohar * modify it under the terms of the GNU General Public License as 83323eec9SMimi Zohar * published by the Free Software Foundation, version 2 of the 93323eec9SMimi Zohar * License. 103323eec9SMimi Zohar * 113323eec9SMimi Zohar * File: ima_api.c 122fe5d6deSMimi Zohar * Implements must_appraise_or_measure, collect_measurement, 132fe5d6deSMimi Zohar * appraise_measurement, store_measurement and store_template. 143323eec9SMimi Zohar */ 155a0e3ad6STejun Heo #include <linux/slab.h> 162fe5d6deSMimi Zohar #include <linux/file.h> 172fe5d6deSMimi Zohar #include <linux/fs.h> 182fe5d6deSMimi Zohar #include <linux/xattr.h> 192fe5d6deSMimi Zohar #include <linux/evm.h> 203b370b21SJeff Layton #include <linux/iversion.h> 211525b06dSDmitry Kasatkin 223323eec9SMimi Zohar #include "ima.h" 232fe5d6deSMimi Zohar 243323eec9SMimi Zohar /* 25a7ed7c60SRoberto Sassu * ima_free_template_entry - free an existing template entry 26a7ed7c60SRoberto Sassu */ 27a7ed7c60SRoberto Sassu void ima_free_template_entry(struct ima_template_entry *entry) 28a7ed7c60SRoberto Sassu { 29a7ed7c60SRoberto Sassu int i; 30a7ed7c60SRoberto Sassu 31a7ed7c60SRoberto Sassu for (i = 0; i < entry->template_desc->num_fields; i++) 32a7ed7c60SRoberto Sassu kfree(entry->template_data[i].data); 33a7ed7c60SRoberto Sassu 34a7ed7c60SRoberto Sassu kfree(entry); 35a7ed7c60SRoberto Sassu } 36a7ed7c60SRoberto Sassu 37a7ed7c60SRoberto Sassu /* 387bc5f447SRoberto Sassu * ima_alloc_init_template - create and initialize a new template entry 397bc5f447SRoberto Sassu */ 4023b57419SRoberto Sassu int ima_alloc_init_template(struct ima_event_data *event_data, 4119453ce0SMatthew Garrett struct ima_template_entry **entry, 4219453ce0SMatthew Garrett struct ima_template_desc *desc) 437bc5f447SRoberto Sassu { 4419453ce0SMatthew Garrett struct ima_template_desc *template_desc; 45a71dc65dSRoberto Sassu int i, result = 0; 467bc5f447SRoberto Sassu 4719453ce0SMatthew Garrett if (desc) 4819453ce0SMatthew Garrett template_desc = desc; 4919453ce0SMatthew Garrett else 5019453ce0SMatthew Garrett template_desc = ima_template_desc_current(); 5119453ce0SMatthew Garrett 52a71dc65dSRoberto Sassu *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * 53a71dc65dSRoberto Sassu sizeof(struct ima_field_data), GFP_NOFS); 54a71dc65dSRoberto Sassu if (!*entry) 557bc5f447SRoberto Sassu return -ENOMEM; 567bc5f447SRoberto Sassu 57a7ed7c60SRoberto Sassu (*entry)->template_desc = template_desc; 58a71dc65dSRoberto Sassu for (i = 0; i < template_desc->num_fields; i++) { 59b2724d58SEric Biggers const struct ima_template_field *field = 60b2724d58SEric Biggers template_desc->fields[i]; 61a71dc65dSRoberto Sassu u32 len; 62a71dc65dSRoberto Sassu 6323b57419SRoberto Sassu result = field->field_init(event_data, 64a71dc65dSRoberto Sassu &((*entry)->template_data[i])); 65a71dc65dSRoberto Sassu if (result != 0) 667bc5f447SRoberto Sassu goto out; 677bc5f447SRoberto Sassu 68a71dc65dSRoberto Sassu len = (*entry)->template_data[i].len; 69a71dc65dSRoberto Sassu (*entry)->template_data_len += sizeof(len); 70a71dc65dSRoberto Sassu (*entry)->template_data_len += len; 717bc5f447SRoberto Sassu } 727bc5f447SRoberto Sassu return 0; 73a71dc65dSRoberto Sassu out: 74a7ed7c60SRoberto Sassu ima_free_template_entry(*entry); 75a71dc65dSRoberto Sassu *entry = NULL; 767bc5f447SRoberto Sassu return result; 777bc5f447SRoberto Sassu } 787bc5f447SRoberto Sassu 797bc5f447SRoberto Sassu /* 803323eec9SMimi Zohar * ima_store_template - store ima template measurements 813323eec9SMimi Zohar * 823323eec9SMimi Zohar * Calculate the hash of a template entry, add the template entry 833323eec9SMimi Zohar * to an ordered list of measurement entries maintained inside the kernel, 843323eec9SMimi Zohar * and also update the aggregate integrity value (maintained inside the 853323eec9SMimi Zohar * configured TPM PCR) over the hashes of the current list of measurement 863323eec9SMimi Zohar * entries. 873323eec9SMimi Zohar * 883323eec9SMimi Zohar * Applications retrieve the current kernel-held measurement list through 893323eec9SMimi Zohar * the securityfs entries in /sys/kernel/security/ima. The signed aggregate 903323eec9SMimi Zohar * TPM PCR (called quote) can be retrieved using a TPM user space library 913323eec9SMimi Zohar * and is used to validate the measurement list. 923323eec9SMimi Zohar * 933323eec9SMimi Zohar * Returns 0 on success, error code otherwise 943323eec9SMimi Zohar */ 953323eec9SMimi Zohar int ima_store_template(struct ima_template_entry *entry, 969803d413SRoberto Sassu int violation, struct inode *inode, 9714b1da85SEric Richter const unsigned char *filename, int pcr) 983323eec9SMimi Zohar { 9952a13284SMimi Zohar static const char op[] = "add_template_measure"; 10052a13284SMimi Zohar static const char audit_cause[] = "hashing_error"; 101a71dc65dSRoberto Sassu char *template_name = entry->template_desc->name; 1023323eec9SMimi Zohar int result; 103a35c3fb6SDmitry Kasatkin struct { 104a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 105140d8022SMimi Zohar char digest[TPM_DIGEST_SIZE]; 106a35c3fb6SDmitry Kasatkin } hash; 1073323eec9SMimi Zohar 1083323eec9SMimi Zohar if (!violation) { 109a71dc65dSRoberto Sassu int num_fields = entry->template_desc->num_fields; 110a71dc65dSRoberto Sassu 111ea593993SDmitry Kasatkin /* this function uses default algo */ 112ea593993SDmitry Kasatkin hash.hdr.algo = HASH_ALGO_SHA1; 113a71dc65dSRoberto Sassu result = ima_calc_field_array_hash(&entry->template_data[0], 114b6f8f16fSRoberto Sassu entry->template_desc, 115a71dc65dSRoberto Sassu num_fields, &hash.hdr); 1163323eec9SMimi Zohar if (result < 0) { 1173323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, 118a71dc65dSRoberto Sassu template_name, op, 1193323eec9SMimi Zohar audit_cause, result, 0); 1203323eec9SMimi Zohar return result; 1213323eec9SMimi Zohar } 122a35c3fb6SDmitry Kasatkin memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); 1233323eec9SMimi Zohar } 12414b1da85SEric Richter entry->pcr = pcr; 1259803d413SRoberto Sassu result = ima_add_template_entry(entry, violation, op, inode, filename); 1263323eec9SMimi Zohar return result; 1273323eec9SMimi Zohar } 1283323eec9SMimi Zohar 1293323eec9SMimi Zohar /* 1303323eec9SMimi Zohar * ima_add_violation - add violation to measurement list. 1313323eec9SMimi Zohar * 1323323eec9SMimi Zohar * Violations are flagged in the measurement list with zero hash values. 1333323eec9SMimi Zohar * By extending the PCR with 0xFF's instead of with zeroes, the PCR 1343323eec9SMimi Zohar * value is invalidated. 1353323eec9SMimi Zohar */ 1367d802a22SRoberto Sassu void ima_add_violation(struct file *file, const unsigned char *filename, 1378d94eb9bSRoberto Sassu struct integrity_iint_cache *iint, 1383323eec9SMimi Zohar const char *op, const char *cause) 1393323eec9SMimi Zohar { 1403323eec9SMimi Zohar struct ima_template_entry *entry; 14131d4b761SLibo Chen struct inode *inode = file_inode(file); 142e038f5f6SThiago Jung Bauermann struct ima_event_data event_data = { .iint = iint, 143e038f5f6SThiago Jung Bauermann .file = file, 144e038f5f6SThiago Jung Bauermann .filename = filename, 145e038f5f6SThiago Jung Bauermann .violation = cause }; 1463323eec9SMimi Zohar int violation = 1; 1473323eec9SMimi Zohar int result; 1483323eec9SMimi Zohar 1493323eec9SMimi Zohar /* can overflow, only indicator */ 1503323eec9SMimi Zohar atomic_long_inc(&ima_htable.violations); 1513323eec9SMimi Zohar 15219453ce0SMatthew Garrett result = ima_alloc_init_template(&event_data, &entry, NULL); 1537bc5f447SRoberto Sassu if (result < 0) { 1543323eec9SMimi Zohar result = -ENOMEM; 1553323eec9SMimi Zohar goto err_out; 1563323eec9SMimi Zohar } 15714b1da85SEric Richter result = ima_store_template(entry, violation, inode, 15814b1da85SEric Richter filename, CONFIG_IMA_MEASURE_PCR_IDX); 1593323eec9SMimi Zohar if (result < 0) 160a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 1613323eec9SMimi Zohar err_out: 1623323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 1633323eec9SMimi Zohar op, cause, result, 0); 1643323eec9SMimi Zohar } 1653323eec9SMimi Zohar 1663323eec9SMimi Zohar /** 167d9d300cdSDmitry Kasatkin * ima_get_action - appraise & measure decision based on policy. 1683323eec9SMimi Zohar * @inode: pointer to inode to measure 169d906c10dSMatthew Garrett * @cred: pointer to credentials structure to validate 170d906c10dSMatthew Garrett * @secid: secid of the task being validated 17120f482abSLans Zhang * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, 17220f482abSLans Zhang * MAY_APPEND) 1734ad87a3dSMimi Zohar * @func: caller identifier 174725de7faSEric Richter * @pcr: pointer filled in if matched measure policy sets pcr= 17519453ce0SMatthew Garrett * @template_desc: pointer filled in if matched measure policy sets template= 1763323eec9SMimi Zohar * 1773323eec9SMimi Zohar * The policy is defined in terms of keypairs: 1783323eec9SMimi Zohar * subj=, obj=, type=, func=, mask=, fsmagic= 1793323eec9SMimi Zohar * subj,obj, and type: are LSM specific. 180d906c10dSMatthew Garrett * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK 181b0935123SPrakhar Srivastava * | KEXEC_CMDLINE 1823323eec9SMimi Zohar * mask: contains the permission mask 1833323eec9SMimi Zohar * fsmagic: hex value 1843323eec9SMimi Zohar * 1852fe5d6deSMimi Zohar * Returns IMA_MEASURE, IMA_APPRAISE mask. 1862fe5d6deSMimi Zohar * 1873323eec9SMimi Zohar */ 188d906c10dSMatthew Garrett int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid, 18919453ce0SMatthew Garrett int mask, enum ima_hooks func, int *pcr, 19019453ce0SMatthew Garrett struct ima_template_desc **template_desc) 1912fe5d6deSMimi Zohar { 192da1b0029SMimi Zohar int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; 1932fe5d6deSMimi Zohar 194fd5f4e90SDmitry Kasatkin flags &= ima_policy_flag; 1952fe5d6deSMimi Zohar 19619453ce0SMatthew Garrett return ima_match_policy(inode, cred, secid, func, mask, flags, pcr, 19719453ce0SMatthew Garrett template_desc); 1982fe5d6deSMimi Zohar } 1992fe5d6deSMimi Zohar 2003323eec9SMimi Zohar /* 2013323eec9SMimi Zohar * ima_collect_measurement - collect file measurement 2023323eec9SMimi Zohar * 2033323eec9SMimi Zohar * Calculate the file hash, if it doesn't already exist, 2043323eec9SMimi Zohar * storing the measurement and i_version in the iint. 2053323eec9SMimi Zohar * 2063323eec9SMimi Zohar * Must be called with iint->mutex held. 2073323eec9SMimi Zohar * 2083323eec9SMimi Zohar * Return 0 on success, error code otherwise 2093323eec9SMimi Zohar */ 210f381c272SMimi Zohar int ima_collect_measurement(struct integrity_iint_cache *iint, 211cf222217SMimi Zohar struct file *file, void *buf, loff_t size, 212cf222217SMimi Zohar enum hash_algo algo) 2133323eec9SMimi Zohar { 214f9b2a735SMimi Zohar const char *audit_cause = "failed"; 215496ad9aaSAl Viro struct inode *inode = file_inode(file); 216b583043eSAl Viro const char *filename = file->f_path.dentry->d_name.name; 2172fe5d6deSMimi Zohar int result = 0; 218f3cc6b25SMimi Zohar int length; 219f3cc6b25SMimi Zohar void *tmpbuf; 220f3cc6b25SMimi Zohar u64 i_version; 221a35c3fb6SDmitry Kasatkin struct { 222a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 223a35c3fb6SDmitry Kasatkin char digest[IMA_MAX_DIGEST_SIZE]; 224a35c3fb6SDmitry Kasatkin } hash; 2253323eec9SMimi Zohar 226f3cc6b25SMimi Zohar if (iint->flags & IMA_COLLECTED) 227f3cc6b25SMimi Zohar goto out; 2283323eec9SMimi Zohar 229f3cc6b25SMimi Zohar /* 230f3cc6b25SMimi Zohar * Dectecting file change is based on i_version. On filesystems 231f3cc6b25SMimi Zohar * which do not support i_version, support is limited to an initial 232f3cc6b25SMimi Zohar * measurement/appraisal/audit. 233f3cc6b25SMimi Zohar */ 2343b370b21SJeff Layton i_version = inode_query_iversion(inode); 235f3cc6b25SMimi Zohar hash.hdr.algo = algo; 236f3cc6b25SMimi Zohar 237f3cc6b25SMimi Zohar /* Initialize hash digest to 0's in case of failure */ 238f3cc6b25SMimi Zohar memset(&hash.digest, 0, sizeof(hash.digest)); 239f3cc6b25SMimi Zohar 240f3cc6b25SMimi Zohar if (buf) 241f3cc6b25SMimi Zohar result = ima_calc_buffer_hash(buf, size, &hash.hdr); 242f3cc6b25SMimi Zohar else 243f3cc6b25SMimi Zohar result = ima_calc_file_hash(file, &hash.hdr); 244f3cc6b25SMimi Zohar 245f3cc6b25SMimi Zohar if (result && result != -EBADF && result != -EINVAL) 246f3cc6b25SMimi Zohar goto out; 247f3cc6b25SMimi Zohar 248f3cc6b25SMimi Zohar length = sizeof(hash.hdr) + hash.hdr.length; 249f3cc6b25SMimi Zohar tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS); 250f3cc6b25SMimi Zohar if (!tmpbuf) { 251f3cc6b25SMimi Zohar result = -ENOMEM; 252f9b2a735SMimi Zohar goto out; 253f9b2a735SMimi Zohar } 254f9b2a735SMimi Zohar 255a35c3fb6SDmitry Kasatkin iint->ima_hash = tmpbuf; 256a35c3fb6SDmitry Kasatkin memcpy(iint->ima_hash, &hash, length); 2573323eec9SMimi Zohar iint->version = i_version; 258f3cc6b25SMimi Zohar 259f3cc6b25SMimi Zohar /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ 260f3cc6b25SMimi Zohar if (!result) 2612fe5d6deSMimi Zohar iint->flags |= IMA_COLLECTED; 262f9b2a735SMimi Zohar out: 263f3cc6b25SMimi Zohar if (result) { 264f3cc6b25SMimi Zohar if (file->f_flags & O_DIRECT) 265f3cc6b25SMimi Zohar audit_cause = "failed(directio)"; 266f3cc6b25SMimi Zohar 2672fe5d6deSMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, 268f9b2a735SMimi Zohar filename, "collect_data", audit_cause, 2692fe5d6deSMimi Zohar result, 0); 270f3cc6b25SMimi Zohar } 2713323eec9SMimi Zohar return result; 2723323eec9SMimi Zohar } 2733323eec9SMimi Zohar 2743323eec9SMimi Zohar /* 2753323eec9SMimi Zohar * ima_store_measurement - store file measurement 2763323eec9SMimi Zohar * 2773323eec9SMimi Zohar * Create an "ima" template and then store the template by calling 2783323eec9SMimi Zohar * ima_store_template. 2793323eec9SMimi Zohar * 2803323eec9SMimi Zohar * We only get here if the inode has not already been measured, 2813323eec9SMimi Zohar * but the measurement could already exist: 2823323eec9SMimi Zohar * - multiple copies of the same file on either the same or 2833323eec9SMimi Zohar * different filesystems. 2843323eec9SMimi Zohar * - the inode was previously flushed as well as the iint info, 2853323eec9SMimi Zohar * containing the hashing info. 2863323eec9SMimi Zohar * 2873323eec9SMimi Zohar * Must be called with iint->mutex held. 2883323eec9SMimi Zohar */ 289f381c272SMimi Zohar void ima_store_measurement(struct integrity_iint_cache *iint, 290bcbc9b0cSMimi Zohar struct file *file, const unsigned char *filename, 291bcbc9b0cSMimi Zohar struct evm_ima_xattr_data *xattr_value, 29219453ce0SMatthew Garrett int xattr_len, int pcr, 29319453ce0SMatthew Garrett struct ima_template_desc *template_desc) 2943323eec9SMimi Zohar { 29552a13284SMimi Zohar static const char op[] = "add_template_measure"; 29652a13284SMimi Zohar static const char audit_cause[] = "ENOMEM"; 2973323eec9SMimi Zohar int result = -ENOMEM; 298496ad9aaSAl Viro struct inode *inode = file_inode(file); 2993323eec9SMimi Zohar struct ima_template_entry *entry; 300e038f5f6SThiago Jung Bauermann struct ima_event_data event_data = { .iint = iint, 301e038f5f6SThiago Jung Bauermann .file = file, 302e038f5f6SThiago Jung Bauermann .filename = filename, 303e038f5f6SThiago Jung Bauermann .xattr_value = xattr_value, 304e038f5f6SThiago Jung Bauermann .xattr_len = xattr_len }; 3053323eec9SMimi Zohar int violation = 0; 3063323eec9SMimi Zohar 307a422638dSEric Richter if (iint->measured_pcrs & (0x1 << pcr)) 3082fe5d6deSMimi Zohar return; 3092fe5d6deSMimi Zohar 31019453ce0SMatthew Garrett result = ima_alloc_init_template(&event_data, &entry, template_desc); 3117bc5f447SRoberto Sassu if (result < 0) { 3123323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 3133323eec9SMimi Zohar op, audit_cause, result, 0); 3143323eec9SMimi Zohar return; 3153323eec9SMimi Zohar } 3163323eec9SMimi Zohar 31714b1da85SEric Richter result = ima_store_template(entry, violation, inode, filename, pcr); 318f3cc6b25SMimi Zohar if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { 3193323eec9SMimi Zohar iint->flags |= IMA_MEASURED; 320a422638dSEric Richter iint->measured_pcrs |= (0x1 << pcr); 321a422638dSEric Richter } 32245fae749SRoberto Sassu if (result < 0) 323a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 3243323eec9SMimi Zohar } 325e7c568e0SPeter Moody 326e7c568e0SPeter Moody void ima_audit_measurement(struct integrity_iint_cache *iint, 327e7c568e0SPeter Moody const unsigned char *filename) 328e7c568e0SPeter Moody { 329e7c568e0SPeter Moody struct audit_buffer *ab; 330e456ef88STycho Andersen char *hash; 3315278aa52SMimi Zohar const char *algo_name = hash_algo_name[iint->ima_hash->algo]; 332e7c568e0SPeter Moody int i; 333e7c568e0SPeter Moody 334e7c568e0SPeter Moody if (iint->flags & IMA_AUDITED) 335e7c568e0SPeter Moody return; 336e7c568e0SPeter Moody 337e456ef88STycho Andersen hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL); 338e456ef88STycho Andersen if (!hash) 339e456ef88STycho Andersen return; 340e456ef88STycho Andersen 341a35c3fb6SDmitry Kasatkin for (i = 0; i < iint->ima_hash->length; i++) 342a35c3fb6SDmitry Kasatkin hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); 343e7c568e0SPeter Moody hash[i * 2] = '\0'; 344e7c568e0SPeter Moody 345cdfb6b34SRichard Guy Briggs ab = audit_log_start(audit_context(), GFP_KERNEL, 346e7c568e0SPeter Moody AUDIT_INTEGRITY_RULE); 347e7c568e0SPeter Moody if (!ab) 348e456ef88STycho Andersen goto out; 349e7c568e0SPeter Moody 350e7c568e0SPeter Moody audit_log_format(ab, "file="); 351e7c568e0SPeter Moody audit_log_untrustedstring(ab, filename); 352e456ef88STycho Andersen audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash); 353e7c568e0SPeter Moody 3542a1fe215SPaul Moore audit_log_task_info(ab); 355e7c568e0SPeter Moody audit_log_end(ab); 356e7c568e0SPeter Moody 357e7c568e0SPeter Moody iint->flags |= IMA_AUDITED; 358e456ef88STycho Andersen out: 359e456ef88STycho Andersen kfree(hash); 360e456ef88STycho Andersen return; 361e7c568e0SPeter Moody } 362ea1046d4SDmitry Kasatkin 363bc15ed66SMimi Zohar /* 364bc15ed66SMimi Zohar * ima_d_path - return a pointer to the full pathname 365bc15ed66SMimi Zohar * 366bc15ed66SMimi Zohar * Attempt to return a pointer to the full pathname for use in the 367bc15ed66SMimi Zohar * IMA measurement list, IMA audit records, and auditing logs. 368bc15ed66SMimi Zohar * 369bc15ed66SMimi Zohar * On failure, return a pointer to a copy of the filename, not dname. 370bc15ed66SMimi Zohar * Returning a pointer to dname, could result in using the pointer 371bc15ed66SMimi Zohar * after the memory has been freed. 372bc15ed66SMimi Zohar */ 373bc15ed66SMimi Zohar const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) 374ea1046d4SDmitry Kasatkin { 375ea1046d4SDmitry Kasatkin char *pathname = NULL; 376ea1046d4SDmitry Kasatkin 377456f5fd3SDmitry Kasatkin *pathbuf = __getname(); 378ea1046d4SDmitry Kasatkin if (*pathbuf) { 37917f4bad3SDmitry Kasatkin pathname = d_absolute_path(path, *pathbuf, PATH_MAX); 380ea1046d4SDmitry Kasatkin if (IS_ERR(pathname)) { 381456f5fd3SDmitry Kasatkin __putname(*pathbuf); 382ea1046d4SDmitry Kasatkin *pathbuf = NULL; 383ea1046d4SDmitry Kasatkin pathname = NULL; 384ea1046d4SDmitry Kasatkin } 385ea1046d4SDmitry Kasatkin } 386bc15ed66SMimi Zohar 387bc15ed66SMimi Zohar if (!pathname) { 388bc15ed66SMimi Zohar strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); 389bc15ed66SMimi Zohar pathname = namebuf; 390bc15ed66SMimi Zohar } 391bc15ed66SMimi Zohar 392bc15ed66SMimi Zohar return pathname; 393ea1046d4SDmitry Kasatkin } 394