1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23323eec9SMimi Zohar /* 33323eec9SMimi Zohar * Copyright (C) 2008 IBM Corporation 43323eec9SMimi Zohar * 53323eec9SMimi Zohar * Author: Mimi Zohar <zohar@us.ibm.com> 63323eec9SMimi Zohar * 73323eec9SMimi Zohar * File: ima_api.c 82fe5d6deSMimi Zohar * Implements must_appraise_or_measure, collect_measurement, 92fe5d6deSMimi Zohar * appraise_measurement, store_measurement and store_template. 103323eec9SMimi Zohar */ 115a0e3ad6STejun Heo #include <linux/slab.h> 122fe5d6deSMimi Zohar #include <linux/file.h> 132fe5d6deSMimi Zohar #include <linux/fs.h> 142fe5d6deSMimi Zohar #include <linux/xattr.h> 152fe5d6deSMimi Zohar #include <linux/evm.h> 163b370b21SJeff Layton #include <linux/iversion.h> 171525b06dSDmitry Kasatkin 183323eec9SMimi Zohar #include "ima.h" 192fe5d6deSMimi Zohar 203323eec9SMimi Zohar /* 21a7ed7c60SRoberto Sassu * ima_free_template_entry - free an existing template entry 22a7ed7c60SRoberto Sassu */ 23a7ed7c60SRoberto Sassu void ima_free_template_entry(struct ima_template_entry *entry) 24a7ed7c60SRoberto Sassu { 25a7ed7c60SRoberto Sassu int i; 26a7ed7c60SRoberto Sassu 27a7ed7c60SRoberto Sassu for (i = 0; i < entry->template_desc->num_fields; i++) 28a7ed7c60SRoberto Sassu kfree(entry->template_data[i].data); 29a7ed7c60SRoberto Sassu 30a7ed7c60SRoberto Sassu kfree(entry); 31a7ed7c60SRoberto Sassu } 32a7ed7c60SRoberto Sassu 33a7ed7c60SRoberto Sassu /* 347bc5f447SRoberto Sassu * ima_alloc_init_template - create and initialize a new template entry 357bc5f447SRoberto Sassu */ 3623b57419SRoberto Sassu int ima_alloc_init_template(struct ima_event_data *event_data, 3719453ce0SMatthew Garrett struct ima_template_entry **entry, 3819453ce0SMatthew Garrett struct ima_template_desc *desc) 397bc5f447SRoberto Sassu { 4019453ce0SMatthew Garrett struct ima_template_desc *template_desc; 41a71dc65dSRoberto Sassu int i, result = 0; 427bc5f447SRoberto Sassu 4319453ce0SMatthew Garrett if (desc) 4419453ce0SMatthew Garrett template_desc = desc; 4519453ce0SMatthew Garrett else 4619453ce0SMatthew Garrett template_desc = ima_template_desc_current(); 4719453ce0SMatthew Garrett 48a71dc65dSRoberto Sassu *entry = kzalloc(sizeof(**entry) + template_desc->num_fields * 49a71dc65dSRoberto Sassu sizeof(struct ima_field_data), GFP_NOFS); 50a71dc65dSRoberto Sassu if (!*entry) 517bc5f447SRoberto Sassu return -ENOMEM; 527bc5f447SRoberto Sassu 53a7ed7c60SRoberto Sassu (*entry)->template_desc = template_desc; 54a71dc65dSRoberto Sassu for (i = 0; i < template_desc->num_fields; i++) { 55b2724d58SEric Biggers const struct ima_template_field *field = 56b2724d58SEric Biggers template_desc->fields[i]; 57a71dc65dSRoberto Sassu u32 len; 58a71dc65dSRoberto Sassu 5923b57419SRoberto Sassu result = field->field_init(event_data, 60a71dc65dSRoberto Sassu &((*entry)->template_data[i])); 61a71dc65dSRoberto Sassu if (result != 0) 627bc5f447SRoberto Sassu goto out; 637bc5f447SRoberto Sassu 64a71dc65dSRoberto Sassu len = (*entry)->template_data[i].len; 65a71dc65dSRoberto Sassu (*entry)->template_data_len += sizeof(len); 66a71dc65dSRoberto Sassu (*entry)->template_data_len += len; 677bc5f447SRoberto Sassu } 687bc5f447SRoberto Sassu return 0; 69a71dc65dSRoberto Sassu out: 70a7ed7c60SRoberto Sassu ima_free_template_entry(*entry); 71a71dc65dSRoberto Sassu *entry = NULL; 727bc5f447SRoberto Sassu return result; 737bc5f447SRoberto Sassu } 747bc5f447SRoberto Sassu 757bc5f447SRoberto Sassu /* 763323eec9SMimi Zohar * ima_store_template - store ima template measurements 773323eec9SMimi Zohar * 783323eec9SMimi Zohar * Calculate the hash of a template entry, add the template entry 793323eec9SMimi Zohar * to an ordered list of measurement entries maintained inside the kernel, 803323eec9SMimi Zohar * and also update the aggregate integrity value (maintained inside the 813323eec9SMimi Zohar * configured TPM PCR) over the hashes of the current list of measurement 823323eec9SMimi Zohar * entries. 833323eec9SMimi Zohar * 843323eec9SMimi Zohar * Applications retrieve the current kernel-held measurement list through 853323eec9SMimi Zohar * the securityfs entries in /sys/kernel/security/ima. The signed aggregate 863323eec9SMimi Zohar * TPM PCR (called quote) can be retrieved using a TPM user space library 873323eec9SMimi Zohar * and is used to validate the measurement list. 883323eec9SMimi Zohar * 893323eec9SMimi Zohar * Returns 0 on success, error code otherwise 903323eec9SMimi Zohar */ 913323eec9SMimi Zohar int ima_store_template(struct ima_template_entry *entry, 929803d413SRoberto Sassu int violation, struct inode *inode, 9314b1da85SEric Richter const unsigned char *filename, int pcr) 943323eec9SMimi Zohar { 9552a13284SMimi Zohar static const char op[] = "add_template_measure"; 9652a13284SMimi Zohar static const char audit_cause[] = "hashing_error"; 97a71dc65dSRoberto Sassu char *template_name = entry->template_desc->name; 983323eec9SMimi Zohar int result; 99a35c3fb6SDmitry Kasatkin struct { 100a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 101140d8022SMimi Zohar char digest[TPM_DIGEST_SIZE]; 102a35c3fb6SDmitry Kasatkin } hash; 1033323eec9SMimi Zohar 1043323eec9SMimi Zohar if (!violation) { 105a71dc65dSRoberto Sassu int num_fields = entry->template_desc->num_fields; 106a71dc65dSRoberto Sassu 107ea593993SDmitry Kasatkin /* this function uses default algo */ 108ea593993SDmitry Kasatkin hash.hdr.algo = HASH_ALGO_SHA1; 109a71dc65dSRoberto Sassu result = ima_calc_field_array_hash(&entry->template_data[0], 110b6f8f16fSRoberto Sassu entry->template_desc, 111a71dc65dSRoberto Sassu num_fields, &hash.hdr); 1123323eec9SMimi Zohar if (result < 0) { 1133323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, 114a71dc65dSRoberto Sassu template_name, op, 1153323eec9SMimi Zohar audit_cause, result, 0); 1163323eec9SMimi Zohar return result; 1173323eec9SMimi Zohar } 118a35c3fb6SDmitry Kasatkin memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); 1193323eec9SMimi Zohar } 12014b1da85SEric Richter entry->pcr = pcr; 1219803d413SRoberto Sassu result = ima_add_template_entry(entry, violation, op, inode, filename); 1223323eec9SMimi Zohar return result; 1233323eec9SMimi Zohar } 1243323eec9SMimi Zohar 1253323eec9SMimi Zohar /* 1263323eec9SMimi Zohar * ima_add_violation - add violation to measurement list. 1273323eec9SMimi Zohar * 1283323eec9SMimi Zohar * Violations are flagged in the measurement list with zero hash values. 1293323eec9SMimi Zohar * By extending the PCR with 0xFF's instead of with zeroes, the PCR 1303323eec9SMimi Zohar * value is invalidated. 1313323eec9SMimi Zohar */ 1327d802a22SRoberto Sassu void ima_add_violation(struct file *file, const unsigned char *filename, 1338d94eb9bSRoberto Sassu struct integrity_iint_cache *iint, 1343323eec9SMimi Zohar const char *op, const char *cause) 1353323eec9SMimi Zohar { 1363323eec9SMimi Zohar struct ima_template_entry *entry; 13731d4b761SLibo Chen struct inode *inode = file_inode(file); 138e038f5f6SThiago Jung Bauermann struct ima_event_data event_data = { .iint = iint, 139e038f5f6SThiago Jung Bauermann .file = file, 140e038f5f6SThiago Jung Bauermann .filename = filename, 141e038f5f6SThiago Jung Bauermann .violation = cause }; 1423323eec9SMimi Zohar int violation = 1; 1433323eec9SMimi Zohar int result; 1443323eec9SMimi Zohar 1453323eec9SMimi Zohar /* can overflow, only indicator */ 1463323eec9SMimi Zohar atomic_long_inc(&ima_htable.violations); 1473323eec9SMimi Zohar 14819453ce0SMatthew Garrett result = ima_alloc_init_template(&event_data, &entry, NULL); 1497bc5f447SRoberto Sassu if (result < 0) { 1503323eec9SMimi Zohar result = -ENOMEM; 1513323eec9SMimi Zohar goto err_out; 1523323eec9SMimi Zohar } 15314b1da85SEric Richter result = ima_store_template(entry, violation, inode, 15414b1da85SEric Richter filename, CONFIG_IMA_MEASURE_PCR_IDX); 1553323eec9SMimi Zohar if (result < 0) 156a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 1573323eec9SMimi Zohar err_out: 1583323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 1593323eec9SMimi Zohar op, cause, result, 0); 1603323eec9SMimi Zohar } 1613323eec9SMimi Zohar 1623323eec9SMimi Zohar /** 163d9d300cdSDmitry Kasatkin * ima_get_action - appraise & measure decision based on policy. 1643323eec9SMimi Zohar * @inode: pointer to inode to measure 165d906c10dSMatthew Garrett * @cred: pointer to credentials structure to validate 166d906c10dSMatthew Garrett * @secid: secid of the task being validated 16720f482abSLans Zhang * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC, 16820f482abSLans Zhang * MAY_APPEND) 1694ad87a3dSMimi Zohar * @func: caller identifier 170725de7faSEric Richter * @pcr: pointer filled in if matched measure policy sets pcr= 17119453ce0SMatthew Garrett * @template_desc: pointer filled in if matched measure policy sets template= 1723323eec9SMimi Zohar * 1733323eec9SMimi Zohar * The policy is defined in terms of keypairs: 1743323eec9SMimi Zohar * subj=, obj=, type=, func=, mask=, fsmagic= 1753323eec9SMimi Zohar * subj,obj, and type: are LSM specific. 176d906c10dSMatthew Garrett * func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK 177b0935123SPrakhar Srivastava * | KEXEC_CMDLINE 1783323eec9SMimi Zohar * mask: contains the permission mask 1793323eec9SMimi Zohar * fsmagic: hex value 1803323eec9SMimi Zohar * 1812fe5d6deSMimi Zohar * Returns IMA_MEASURE, IMA_APPRAISE mask. 1822fe5d6deSMimi Zohar * 1833323eec9SMimi Zohar */ 184d906c10dSMatthew Garrett int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid, 18519453ce0SMatthew Garrett int mask, enum ima_hooks func, int *pcr, 18619453ce0SMatthew Garrett struct ima_template_desc **template_desc) 1872fe5d6deSMimi Zohar { 188da1b0029SMimi Zohar int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH; 1892fe5d6deSMimi Zohar 190fd5f4e90SDmitry Kasatkin flags &= ima_policy_flag; 1912fe5d6deSMimi Zohar 19219453ce0SMatthew Garrett return ima_match_policy(inode, cred, secid, func, mask, flags, pcr, 19319453ce0SMatthew Garrett template_desc); 1942fe5d6deSMimi Zohar } 1952fe5d6deSMimi Zohar 1963323eec9SMimi Zohar /* 1973323eec9SMimi Zohar * ima_collect_measurement - collect file measurement 1983323eec9SMimi Zohar * 1993323eec9SMimi Zohar * Calculate the file hash, if it doesn't already exist, 2003323eec9SMimi Zohar * storing the measurement and i_version in the iint. 2013323eec9SMimi Zohar * 2023323eec9SMimi Zohar * Must be called with iint->mutex held. 2033323eec9SMimi Zohar * 2043323eec9SMimi Zohar * Return 0 on success, error code otherwise 2053323eec9SMimi Zohar */ 206f381c272SMimi Zohar int ima_collect_measurement(struct integrity_iint_cache *iint, 207cf222217SMimi Zohar struct file *file, void *buf, loff_t size, 20815588227SThiago Jung Bauermann enum hash_algo algo, struct modsig *modsig) 2093323eec9SMimi Zohar { 210f9b2a735SMimi Zohar const char *audit_cause = "failed"; 211496ad9aaSAl Viro struct inode *inode = file_inode(file); 212b583043eSAl Viro const char *filename = file->f_path.dentry->d_name.name; 2132fe5d6deSMimi Zohar int result = 0; 214f3cc6b25SMimi Zohar int length; 215f3cc6b25SMimi Zohar void *tmpbuf; 216f3cc6b25SMimi Zohar u64 i_version; 217a35c3fb6SDmitry Kasatkin struct { 218a35c3fb6SDmitry Kasatkin struct ima_digest_data hdr; 219a35c3fb6SDmitry Kasatkin char digest[IMA_MAX_DIGEST_SIZE]; 220a35c3fb6SDmitry Kasatkin } hash; 2213323eec9SMimi Zohar 222f3cc6b25SMimi Zohar if (iint->flags & IMA_COLLECTED) 223f3cc6b25SMimi Zohar goto out; 2243323eec9SMimi Zohar 225f3cc6b25SMimi Zohar /* 226f3cc6b25SMimi Zohar * Dectecting file change is based on i_version. On filesystems 227f3cc6b25SMimi Zohar * which do not support i_version, support is limited to an initial 228f3cc6b25SMimi Zohar * measurement/appraisal/audit. 229f3cc6b25SMimi Zohar */ 2303b370b21SJeff Layton i_version = inode_query_iversion(inode); 231f3cc6b25SMimi Zohar hash.hdr.algo = algo; 232f3cc6b25SMimi Zohar 233f3cc6b25SMimi Zohar /* Initialize hash digest to 0's in case of failure */ 234f3cc6b25SMimi Zohar memset(&hash.digest, 0, sizeof(hash.digest)); 235f3cc6b25SMimi Zohar 236f3cc6b25SMimi Zohar if (buf) 237f3cc6b25SMimi Zohar result = ima_calc_buffer_hash(buf, size, &hash.hdr); 238f3cc6b25SMimi Zohar else 239f3cc6b25SMimi Zohar result = ima_calc_file_hash(file, &hash.hdr); 240f3cc6b25SMimi Zohar 241f3cc6b25SMimi Zohar if (result && result != -EBADF && result != -EINVAL) 242f3cc6b25SMimi Zohar goto out; 243f3cc6b25SMimi Zohar 244f3cc6b25SMimi Zohar length = sizeof(hash.hdr) + hash.hdr.length; 245f3cc6b25SMimi Zohar tmpbuf = krealloc(iint->ima_hash, length, GFP_NOFS); 246f3cc6b25SMimi Zohar if (!tmpbuf) { 247f3cc6b25SMimi Zohar result = -ENOMEM; 248f9b2a735SMimi Zohar goto out; 249f9b2a735SMimi Zohar } 250f9b2a735SMimi Zohar 251a35c3fb6SDmitry Kasatkin iint->ima_hash = tmpbuf; 252a35c3fb6SDmitry Kasatkin memcpy(iint->ima_hash, &hash, length); 2533323eec9SMimi Zohar iint->version = i_version; 254f3cc6b25SMimi Zohar 25515588227SThiago Jung Bauermann if (modsig) 25615588227SThiago Jung Bauermann ima_collect_modsig(modsig, buf, size); 25715588227SThiago Jung Bauermann 258f3cc6b25SMimi Zohar /* Possibly temporary failure due to type of read (eg. O_DIRECT) */ 259f3cc6b25SMimi Zohar if (!result) 2602fe5d6deSMimi Zohar iint->flags |= IMA_COLLECTED; 261f9b2a735SMimi Zohar out: 262f3cc6b25SMimi Zohar if (result) { 263f3cc6b25SMimi Zohar if (file->f_flags & O_DIRECT) 264f3cc6b25SMimi Zohar audit_cause = "failed(directio)"; 265f3cc6b25SMimi Zohar 2662fe5d6deSMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, 267f9b2a735SMimi Zohar filename, "collect_data", audit_cause, 2682fe5d6deSMimi Zohar result, 0); 269f3cc6b25SMimi Zohar } 2703323eec9SMimi Zohar return result; 2713323eec9SMimi Zohar } 2723323eec9SMimi Zohar 2733323eec9SMimi Zohar /* 2743323eec9SMimi Zohar * ima_store_measurement - store file measurement 2753323eec9SMimi Zohar * 2763323eec9SMimi Zohar * Create an "ima" template and then store the template by calling 2773323eec9SMimi Zohar * ima_store_template. 2783323eec9SMimi Zohar * 2793323eec9SMimi Zohar * We only get here if the inode has not already been measured, 2803323eec9SMimi Zohar * but the measurement could already exist: 2813323eec9SMimi Zohar * - multiple copies of the same file on either the same or 2823323eec9SMimi Zohar * different filesystems. 2833323eec9SMimi Zohar * - the inode was previously flushed as well as the iint info, 2843323eec9SMimi Zohar * containing the hashing info. 2853323eec9SMimi Zohar * 2863323eec9SMimi Zohar * Must be called with iint->mutex held. 2873323eec9SMimi Zohar */ 288f381c272SMimi Zohar void ima_store_measurement(struct integrity_iint_cache *iint, 289bcbc9b0cSMimi Zohar struct file *file, const unsigned char *filename, 290bcbc9b0cSMimi Zohar struct evm_ima_xattr_data *xattr_value, 29119453ce0SMatthew Garrett int xattr_len, int pcr, 29219453ce0SMatthew Garrett struct ima_template_desc *template_desc) 2933323eec9SMimi Zohar { 29452a13284SMimi Zohar static const char op[] = "add_template_measure"; 29552a13284SMimi Zohar static const char audit_cause[] = "ENOMEM"; 2963323eec9SMimi Zohar int result = -ENOMEM; 297496ad9aaSAl Viro struct inode *inode = file_inode(file); 2983323eec9SMimi Zohar struct ima_template_entry *entry; 299e038f5f6SThiago Jung Bauermann struct ima_event_data event_data = { .iint = iint, 300e038f5f6SThiago Jung Bauermann .file = file, 301e038f5f6SThiago Jung Bauermann .filename = filename, 302e038f5f6SThiago Jung Bauermann .xattr_value = xattr_value, 303e038f5f6SThiago Jung Bauermann .xattr_len = xattr_len }; 3043323eec9SMimi Zohar int violation = 0; 3053323eec9SMimi Zohar 306a422638dSEric Richter if (iint->measured_pcrs & (0x1 << pcr)) 3072fe5d6deSMimi Zohar return; 3082fe5d6deSMimi Zohar 30919453ce0SMatthew Garrett result = ima_alloc_init_template(&event_data, &entry, template_desc); 3107bc5f447SRoberto Sassu if (result < 0) { 3113323eec9SMimi Zohar integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, 3123323eec9SMimi Zohar op, audit_cause, result, 0); 3133323eec9SMimi Zohar return; 3143323eec9SMimi Zohar } 3153323eec9SMimi Zohar 31614b1da85SEric Richter result = ima_store_template(entry, violation, inode, filename, pcr); 317f3cc6b25SMimi Zohar if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { 3183323eec9SMimi Zohar iint->flags |= IMA_MEASURED; 319a422638dSEric Richter iint->measured_pcrs |= (0x1 << pcr); 320a422638dSEric Richter } 32145fae749SRoberto Sassu if (result < 0) 322a7ed7c60SRoberto Sassu ima_free_template_entry(entry); 3233323eec9SMimi Zohar } 324e7c568e0SPeter Moody 325e7c568e0SPeter Moody void ima_audit_measurement(struct integrity_iint_cache *iint, 326e7c568e0SPeter Moody const unsigned char *filename) 327e7c568e0SPeter Moody { 328e7c568e0SPeter Moody struct audit_buffer *ab; 329e456ef88STycho Andersen char *hash; 3305278aa52SMimi Zohar const char *algo_name = hash_algo_name[iint->ima_hash->algo]; 331e7c568e0SPeter Moody int i; 332e7c568e0SPeter Moody 333e7c568e0SPeter Moody if (iint->flags & IMA_AUDITED) 334e7c568e0SPeter Moody return; 335e7c568e0SPeter Moody 336e456ef88STycho Andersen hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL); 337e456ef88STycho Andersen if (!hash) 338e456ef88STycho Andersen return; 339e456ef88STycho Andersen 340a35c3fb6SDmitry Kasatkin for (i = 0; i < iint->ima_hash->length; i++) 341a35c3fb6SDmitry Kasatkin hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]); 342e7c568e0SPeter Moody hash[i * 2] = '\0'; 343e7c568e0SPeter Moody 344cdfb6b34SRichard Guy Briggs ab = audit_log_start(audit_context(), GFP_KERNEL, 345e7c568e0SPeter Moody AUDIT_INTEGRITY_RULE); 346e7c568e0SPeter Moody if (!ab) 347e456ef88STycho Andersen goto out; 348e7c568e0SPeter Moody 349e7c568e0SPeter Moody audit_log_format(ab, "file="); 350e7c568e0SPeter Moody audit_log_untrustedstring(ab, filename); 351e456ef88STycho Andersen audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash); 352e7c568e0SPeter Moody 3532a1fe215SPaul Moore audit_log_task_info(ab); 354e7c568e0SPeter Moody audit_log_end(ab); 355e7c568e0SPeter Moody 356e7c568e0SPeter Moody iint->flags |= IMA_AUDITED; 357e456ef88STycho Andersen out: 358e456ef88STycho Andersen kfree(hash); 359e456ef88STycho Andersen return; 360e7c568e0SPeter Moody } 361ea1046d4SDmitry Kasatkin 362bc15ed66SMimi Zohar /* 363bc15ed66SMimi Zohar * ima_d_path - return a pointer to the full pathname 364bc15ed66SMimi Zohar * 365bc15ed66SMimi Zohar * Attempt to return a pointer to the full pathname for use in the 366bc15ed66SMimi Zohar * IMA measurement list, IMA audit records, and auditing logs. 367bc15ed66SMimi Zohar * 368bc15ed66SMimi Zohar * On failure, return a pointer to a copy of the filename, not dname. 369bc15ed66SMimi Zohar * Returning a pointer to dname, could result in using the pointer 370bc15ed66SMimi Zohar * after the memory has been freed. 371bc15ed66SMimi Zohar */ 372bc15ed66SMimi Zohar const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) 373ea1046d4SDmitry Kasatkin { 374ea1046d4SDmitry Kasatkin char *pathname = NULL; 375ea1046d4SDmitry Kasatkin 376456f5fd3SDmitry Kasatkin *pathbuf = __getname(); 377ea1046d4SDmitry Kasatkin if (*pathbuf) { 37817f4bad3SDmitry Kasatkin pathname = d_absolute_path(path, *pathbuf, PATH_MAX); 379ea1046d4SDmitry Kasatkin if (IS_ERR(pathname)) { 380456f5fd3SDmitry Kasatkin __putname(*pathbuf); 381ea1046d4SDmitry Kasatkin *pathbuf = NULL; 382ea1046d4SDmitry Kasatkin pathname = NULL; 383ea1046d4SDmitry Kasatkin } 384ea1046d4SDmitry Kasatkin } 385bc15ed66SMimi Zohar 386bc15ed66SMimi Zohar if (!pathname) { 387bc15ed66SMimi Zohar strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); 388bc15ed66SMimi Zohar pathname = namebuf; 389bc15ed66SMimi Zohar } 390bc15ed66SMimi Zohar 391bc15ed66SMimi Zohar return pathname; 392ea1046d4SDmitry Kasatkin } 393