2019-05-24 13:04:05 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2021-05-07 04:06:44 +03:00
/*
2005-12-16 01:29:43 +03:00
* file . c - operations for regular ( text ) files .
*
* Based on sysfs :
* sysfs is Copyright ( C ) 2001 , 2002 , 2003 Patrick Mochel
*
* configfs Copyright ( C ) 2005 Oracle . All rights reserved .
*/
# include <linux/fs.h>
# include <linux/module.h>
# include <linux/slab.h>
2007-06-22 13:20:00 +04:00
# include <linux/mutex.h>
2015-10-22 23:30:04 +03:00
# include <linux/vmalloc.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2021-05-25 11:30:21 +03:00
# include <linux/uio.h>
2005-12-16 01:29:43 +03:00
# include <linux/configfs.h>
# include "configfs_internal.h"
2007-06-23 00:07:02 +04:00
/*
* A simple attribute can only be 4096 characters . Why 4 k ? Because the
* original code limited it to PAGE_SIZE . That ' s a bad idea , though ,
* because an attribute of 16 k on ia64 won ' t work on x86 . So we limit to
* 4 k , our minimum common page size .
*/
# define SIMPLE_ATTR_SIZE 4096
2005-12-16 01:29:43 +03:00
struct configfs_buffer {
size_t count ;
loff_t pos ;
char * page ;
struct configfs_item_operations * ops ;
2007-06-22 13:20:00 +04:00
struct mutex mutex ;
2005-12-16 01:29:43 +03:00
int needs_read_fill ;
2015-10-22 23:30:04 +03:00
bool read_in_progress ;
bool write_in_progress ;
char * bin_buffer ;
int bin_buffer_size ;
2019-08-30 18:30:03 +03:00
int cb_max_size ;
struct config_item * item ;
struct module * owner ;
union {
struct configfs_attribute * attr ;
struct configfs_bin_attribute * bin_attr ;
} ;
2005-12-16 01:29:43 +03:00
} ;
2019-08-31 10:43:43 +03:00
static inline struct configfs_fragment * to_frag ( struct file * file )
{
struct configfs_dirent * sd = file - > f_path . dentry - > d_fsdata ;
return sd - > s_frag ;
}
2005-12-16 01:29:43 +03:00
2019-08-31 10:43:43 +03:00
static int fill_read_buffer ( struct file * file , struct configfs_buffer * buffer )
2005-12-16 01:29:43 +03:00
{
2019-08-31 10:43:43 +03:00
struct configfs_fragment * frag = to_frag ( file ) ;
ssize_t count = - ENOENT ;
2005-12-16 01:29:43 +03:00
if ( ! buffer - > page )
buffer - > page = ( char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! buffer - > page )
return - ENOMEM ;
2019-08-31 10:43:43 +03:00
down_read ( & frag - > frag_sem ) ;
if ( ! frag - > frag_dead )
count = buffer - > attr - > show ( buffer - > item , buffer - > page ) ;
up_read ( & frag - > frag_sem ) ;
2019-08-30 18:30:03 +03:00
if ( count < 0 )
return count ;
if ( WARN_ON_ONCE ( count > ( ssize_t ) SIMPLE_ATTR_SIZE ) )
return - EIO ;
buffer - > needs_read_fill = 0 ;
buffer - > count = count ;
return 0 ;
2005-12-16 01:29:43 +03:00
}
2021-05-25 11:30:21 +03:00
static ssize_t configfs_read_iter ( struct kiocb * iocb , struct iov_iter * to )
2005-12-16 01:29:43 +03:00
{
2021-05-25 11:30:21 +03:00
struct file * file = iocb - > ki_filp ;
2019-08-30 18:30:03 +03:00
struct configfs_buffer * buffer = file - > private_data ;
2005-12-16 01:29:43 +03:00
ssize_t retval = 0 ;
2007-06-22 13:20:00 +04:00
mutex_lock ( & buffer - > mutex ) ;
2005-12-16 01:29:43 +03:00
if ( buffer - > needs_read_fill ) {
2019-08-31 10:43:43 +03:00
retval = fill_read_buffer ( file , buffer ) ;
2019-08-30 18:30:03 +03:00
if ( retval )
2005-12-16 01:29:43 +03:00
goto out ;
}
2021-05-25 11:30:21 +03:00
pr_debug ( " %s: count = %zd, pos = %lld, buf = %s \n " ,
__func__ , iov_iter_count ( to ) , iocb - > ki_pos , buffer - > page ) ;
2021-07-13 20:49:26 +03:00
if ( iocb - > ki_pos > = buffer - > count )
goto out ;
retval = copy_to_iter ( buffer - > page + iocb - > ki_pos ,
buffer - > count - iocb - > ki_pos , to ) ;
2021-05-25 11:30:21 +03:00
iocb - > ki_pos + = retval ;
if ( retval = = 0 )
retval = - EFAULT ;
2005-12-16 01:29:43 +03:00
out :
2007-06-22 13:20:00 +04:00
mutex_unlock ( & buffer - > mutex ) ;
2005-12-16 01:29:43 +03:00
return retval ;
}
2021-05-25 11:30:21 +03:00
static ssize_t configfs_bin_read_iter ( struct kiocb * iocb , struct iov_iter * to )
2015-10-22 23:30:04 +03:00
{
2021-05-25 11:30:21 +03:00
struct file * file = iocb - > ki_filp ;
2019-08-31 10:43:43 +03:00
struct configfs_fragment * frag = to_frag ( file ) ;
2015-10-22 23:30:04 +03:00
struct configfs_buffer * buffer = file - > private_data ;
ssize_t retval = 0 ;
2021-05-25 11:30:21 +03:00
ssize_t len ;
2015-10-22 23:30:04 +03:00
mutex_lock ( & buffer - > mutex ) ;
/* we don't support switching read/write modes */
if ( buffer - > write_in_progress ) {
retval = - ETXTBSY ;
goto out ;
}
2017-10-07 17:02:21 +03:00
buffer - > read_in_progress = true ;
2015-10-22 23:30:04 +03:00
if ( buffer - > needs_read_fill ) {
/* perform first read with buf == NULL to get extent */
2019-08-31 10:43:43 +03:00
down_read ( & frag - > frag_sem ) ;
if ( ! frag - > frag_dead )
len = buffer - > bin_attr - > read ( buffer - > item , NULL , 0 ) ;
else
len = - ENOENT ;
up_read ( & frag - > frag_sem ) ;
2015-10-22 23:30:04 +03:00
if ( len < = 0 ) {
retval = len ;
goto out ;
}
/* do not exceed the maximum value */
2019-08-30 18:30:03 +03:00
if ( buffer - > cb_max_size & & len > buffer - > cb_max_size ) {
2015-10-22 23:30:04 +03:00
retval = - EFBIG ;
goto out ;
}
buffer - > bin_buffer = vmalloc ( len ) ;
if ( buffer - > bin_buffer = = NULL ) {
retval = - ENOMEM ;
goto out ;
}
buffer - > bin_buffer_size = len ;
/* perform second read to fill buffer */
2019-08-31 10:43:43 +03:00
down_read ( & frag - > frag_sem ) ;
if ( ! frag - > frag_dead )
len = buffer - > bin_attr - > read ( buffer - > item ,
buffer - > bin_buffer , len ) ;
else
len = - ENOENT ;
up_read ( & frag - > frag_sem ) ;
2015-10-22 23:30:04 +03:00
if ( len < 0 ) {
retval = len ;
vfree ( buffer - > bin_buffer ) ;
buffer - > bin_buffer_size = 0 ;
buffer - > bin_buffer = NULL ;
goto out ;
}
buffer - > needs_read_fill = 0 ;
}
2021-07-13 20:49:26 +03:00
if ( iocb - > ki_pos > = buffer - > bin_buffer_size )
goto out ;
retval = copy_to_iter ( buffer - > bin_buffer + iocb - > ki_pos ,
buffer - > bin_buffer_size - iocb - > ki_pos , to ) ;
2021-05-25 11:30:21 +03:00
iocb - > ki_pos + = retval ;
if ( retval = = 0 )
retval = - EFAULT ;
2015-10-22 23:30:04 +03:00
out :
mutex_unlock ( & buffer - > mutex ) ;
return retval ;
}
2021-07-13 20:49:26 +03:00
/* Fill [buffer, buffer + pos) with data coming from @from. */
static int fill_write_buffer ( struct configfs_buffer * buffer , loff_t pos ,
2021-05-25 11:30:21 +03:00
struct iov_iter * from )
2005-12-16 01:29:43 +03:00
{
2021-07-13 20:49:26 +03:00
loff_t to_copy ;
2021-05-25 11:30:21 +03:00
int copied ;
2021-07-13 20:49:26 +03:00
u8 * to ;
2005-12-16 01:29:43 +03:00
if ( ! buffer - > page )
2007-01-24 04:00:45 +03:00
buffer - > page = ( char * ) __get_free_pages ( GFP_KERNEL , 0 ) ;
2005-12-16 01:29:43 +03:00
if ( ! buffer - > page )
return - ENOMEM ;
2021-07-13 20:49:26 +03:00
to_copy = SIMPLE_ATTR_SIZE - 1 - pos ;
if ( to_copy < = 0 )
return 0 ;
to = buffer - > page + pos ;
copied = copy_from_iter ( to , to_copy , from ) ;
2005-12-16 01:29:43 +03:00
buffer - > needs_read_fill = 1 ;
2007-01-24 04:00:45 +03:00
/* if buf is assumed to contain a string, terminate it by \0,
* so e . g . sscanf ( ) can scan the string easily */
2021-07-13 20:49:26 +03:00
to [ copied ] = 0 ;
2021-05-25 11:30:21 +03:00
return copied ? : - EFAULT ;
2005-12-16 01:29:43 +03:00
}
static int
2019-08-31 10:43:43 +03:00
flush_write_buffer ( struct file * file , struct configfs_buffer * buffer , size_t count )
2005-12-16 01:29:43 +03:00
{
2019-08-31 10:43:43 +03:00
struct configfs_fragment * frag = to_frag ( file ) ;
int res = - ENOENT ;
down_read ( & frag - > frag_sem ) ;
if ( ! frag - > frag_dead )
res = buffer - > attr - > store ( buffer - > item , buffer - > page , count ) ;
up_read ( & frag - > frag_sem ) ;
return res ;
2005-12-16 01:29:43 +03:00
}
2021-05-25 11:28:54 +03:00
/*
* There is no easy way for us to know if userspace is only doing a partial
* write , so we don ' t support them . We expect the entire buffer to come on the
* first write .
* Hint : if you ' re writing a value , first read the file , modify only the value
* you ' re changing , then write entire buffer back .
2005-12-16 01:29:43 +03:00
*/
2021-05-25 11:30:21 +03:00
static ssize_t configfs_write_iter ( struct kiocb * iocb , struct iov_iter * from )
2005-12-16 01:29:43 +03:00
{
2021-05-25 11:30:21 +03:00
struct file * file = iocb - > ki_filp ;
2019-08-30 18:30:03 +03:00
struct configfs_buffer * buffer = file - > private_data ;
2006-01-26 00:31:07 +03:00
ssize_t len ;
2005-12-16 01:29:43 +03:00
2007-06-22 13:20:00 +04:00
mutex_lock ( & buffer - > mutex ) ;
2021-07-13 20:49:26 +03:00
len = fill_write_buffer ( buffer , iocb - > ki_pos , from ) ;
2006-01-26 00:31:07 +03:00
if ( len > 0 )
2019-08-31 10:43:43 +03:00
len = flush_write_buffer ( file , buffer , len ) ;
2006-01-26 00:31:07 +03:00
if ( len > 0 )
2021-05-25 11:30:21 +03:00
iocb - > ki_pos + = len ;
2007-06-22 13:20:00 +04:00
mutex_unlock ( & buffer - > mutex ) ;
2006-01-26 00:31:07 +03:00
return len ;
2005-12-16 01:29:43 +03:00
}
2021-05-25 11:30:21 +03:00
static ssize_t configfs_bin_write_iter ( struct kiocb * iocb ,
struct iov_iter * from )
2015-10-22 23:30:04 +03:00
{
2021-05-25 11:30:21 +03:00
struct file * file = iocb - > ki_filp ;
2015-10-22 23:30:04 +03:00
struct configfs_buffer * buffer = file - > private_data ;
void * tbuf = NULL ;
2021-05-25 11:30:21 +03:00
size_t end_offset ;
2015-10-22 23:30:04 +03:00
ssize_t len ;
mutex_lock ( & buffer - > mutex ) ;
/* we don't support switching read/write modes */
if ( buffer - > read_in_progress ) {
len = - ETXTBSY ;
goto out ;
}
2017-10-07 17:02:21 +03:00
buffer - > write_in_progress = true ;
2015-10-22 23:30:04 +03:00
/* buffer grows? */
2021-05-25 11:30:21 +03:00
end_offset = iocb - > ki_pos + iov_iter_count ( from ) ;
if ( end_offset > buffer - > bin_buffer_size ) {
if ( buffer - > cb_max_size & & end_offset > buffer - > cb_max_size ) {
2015-10-22 23:30:04 +03:00
len = - EFBIG ;
2016-09-15 19:20:12 +03:00
goto out ;
2015-10-22 23:30:04 +03:00
}
2021-05-25 11:30:21 +03:00
tbuf = vmalloc ( end_offset ) ;
2015-10-22 23:30:04 +03:00
if ( tbuf = = NULL ) {
len = - ENOMEM ;
goto out ;
}
/* copy old contents */
if ( buffer - > bin_buffer ) {
memcpy ( tbuf , buffer - > bin_buffer ,
buffer - > bin_buffer_size ) ;
vfree ( buffer - > bin_buffer ) ;
}
/* clear the new area */
memset ( tbuf + buffer - > bin_buffer_size , 0 ,
2021-05-25 11:30:21 +03:00
end_offset - buffer - > bin_buffer_size ) ;
2015-10-22 23:30:04 +03:00
buffer - > bin_buffer = tbuf ;
2021-05-25 11:30:21 +03:00
buffer - > bin_buffer_size = end_offset ;
2015-10-22 23:30:04 +03:00
}
2021-07-13 20:49:26 +03:00
len = copy_from_iter ( buffer - > bin_buffer + iocb - > ki_pos ,
buffer - > bin_buffer_size - iocb - > ki_pos , from ) ;
iocb - > ki_pos + = len ;
2015-10-22 23:30:04 +03:00
out :
mutex_unlock ( & buffer - > mutex ) ;
2021-05-25 11:30:21 +03:00
return len ? : - EFAULT ;
2015-10-22 23:30:04 +03:00
}
2019-08-30 18:30:03 +03:00
static int __configfs_open_file ( struct inode * inode , struct file * file , int type )
2005-12-16 01:29:43 +03:00
{
2019-08-30 18:30:03 +03:00
struct dentry * dentry = file - > f_path . dentry ;
2019-08-31 10:43:43 +03:00
struct configfs_fragment * frag = to_frag ( file ) ;
2019-08-30 18:30:03 +03:00
struct configfs_attribute * attr ;
struct configfs_buffer * buffer ;
int error ;
2005-12-16 01:29:43 +03:00
2019-08-30 18:30:03 +03:00
error = - ENOMEM ;
buffer = kzalloc ( sizeof ( struct configfs_buffer ) , GFP_KERNEL ) ;
if ( ! buffer )
goto out ;
2005-12-16 01:29:43 +03:00
2019-08-31 10:43:43 +03:00
error = - ENOENT ;
down_read ( & frag - > frag_sem ) ;
if ( unlikely ( frag - > frag_dead ) )
goto out_free_buffer ;
2019-08-30 18:30:03 +03:00
error = - EINVAL ;
2019-08-31 10:43:43 +03:00
buffer - > item = to_item ( dentry - > d_parent ) ;
2019-08-30 18:30:03 +03:00
if ( ! buffer - > item )
goto out_free_buffer ;
attr = to_attr ( dentry ) ;
if ( ! attr )
2021-03-01 09:10:53 +03:00
goto out_free_buffer ;
2019-08-30 18:30:03 +03:00
if ( type & CONFIGFS_ITEM_BIN_ATTR ) {
buffer - > bin_attr = to_bin_attr ( dentry ) ;
buffer - > cb_max_size = buffer - > bin_attr - > cb_max_size ;
} else {
buffer - > attr = attr ;
}
2015-10-22 23:30:04 +03:00
2019-08-30 18:30:03 +03:00
buffer - > owner = attr - > ca_owner ;
2005-12-16 01:29:43 +03:00
/* Grab the module reference for this attribute if we have one */
2019-08-30 18:30:03 +03:00
error = - ENODEV ;
if ( ! try_module_get ( buffer - > owner ) )
2021-03-01 09:10:53 +03:00
goto out_free_buffer ;
2005-12-16 01:29:43 +03:00
2019-08-30 18:30:03 +03:00
error = - EACCES ;
if ( ! buffer - > item - > ci_type )
goto out_put_module ;
buffer - > ops = buffer - > item - > ci_type - > ct_item_ops ;
2005-12-16 01:29:43 +03:00
/* File needs write support.
* The inode ' s perms must say it ' s ok ,
* and we must have a store method .
*/
if ( file - > f_mode & FMODE_WRITE ) {
2015-10-22 23:30:04 +03:00
if ( ! ( inode - > i_mode & S_IWUGO ) )
2019-08-30 18:30:03 +03:00
goto out_put_module ;
2015-10-22 23:30:04 +03:00
if ( ( type & CONFIGFS_ITEM_ATTR ) & & ! attr - > store )
2019-08-30 18:30:03 +03:00
goto out_put_module ;
if ( ( type & CONFIGFS_ITEM_BIN_ATTR ) & & ! buffer - > bin_attr - > write )
goto out_put_module ;
2005-12-16 01:29:43 +03:00
}
/* File needs read support.
* The inode ' s perms must say it ' s ok , and we there
* must be a show method for it .
*/
if ( file - > f_mode & FMODE_READ ) {
2015-10-22 23:30:04 +03:00
if ( ! ( inode - > i_mode & S_IRUGO ) )
2019-08-30 18:30:03 +03:00
goto out_put_module ;
2015-10-22 23:30:04 +03:00
if ( ( type & CONFIGFS_ITEM_ATTR ) & & ! attr - > show )
2019-08-30 18:30:03 +03:00
goto out_put_module ;
if ( ( type & CONFIGFS_ITEM_BIN_ATTR ) & & ! buffer - > bin_attr - > read )
goto out_put_module ;
2005-12-16 01:29:43 +03:00
}
2007-06-22 13:20:00 +04:00
mutex_init ( & buffer - > mutex ) ;
2006-10-11 02:15:55 +04:00
buffer - > needs_read_fill = 1 ;
2017-10-07 17:02:21 +03:00
buffer - > read_in_progress = false ;
buffer - > write_in_progress = false ;
2006-10-11 02:15:55 +04:00
file - > private_data = buffer ;
2019-08-31 10:43:43 +03:00
up_read ( & frag - > frag_sem ) ;
2019-08-30 18:30:03 +03:00
return 0 ;
2005-12-16 01:29:43 +03:00
2019-08-30 18:30:03 +03:00
out_put_module :
module_put ( buffer - > owner ) ;
out_free_buffer :
2019-08-31 10:43:43 +03:00
up_read ( & frag - > frag_sem ) ;
2019-08-30 18:30:03 +03:00
kfree ( buffer ) ;
out :
2005-12-16 01:29:43 +03:00
return error ;
}
2015-10-22 23:30:04 +03:00
static int configfs_release ( struct inode * inode , struct file * filp )
2005-12-16 01:29:43 +03:00
{
2019-08-30 18:30:03 +03:00
struct configfs_buffer * buffer = filp - > private_data ;
module_put ( buffer - > owner ) ;
if ( buffer - > page )
free_page ( ( unsigned long ) buffer - > page ) ;
mutex_destroy ( & buffer - > mutex ) ;
kfree ( buffer ) ;
2005-12-16 01:29:43 +03:00
return 0 ;
}
2015-10-22 23:30:04 +03:00
static int configfs_open_file ( struct inode * inode , struct file * filp )
{
2019-08-30 18:30:03 +03:00
return __configfs_open_file ( inode , filp , CONFIGFS_ITEM_ATTR ) ;
2015-10-22 23:30:04 +03:00
}
static int configfs_open_bin_file ( struct inode * inode , struct file * filp )
{
2019-08-30 18:30:03 +03:00
return __configfs_open_file ( inode , filp , CONFIGFS_ITEM_BIN_ATTR ) ;
2015-10-22 23:30:04 +03:00
}
2019-08-30 18:30:03 +03:00
static int configfs_release_bin_file ( struct inode * inode , struct file * file )
2015-10-22 23:30:04 +03:00
{
2019-08-30 18:30:03 +03:00
struct configfs_buffer * buffer = file - > private_data ;
2015-10-22 23:30:04 +03:00
if ( buffer - > write_in_progress ) {
2019-08-31 10:43:43 +03:00
struct configfs_fragment * frag = to_frag ( file ) ;
2015-10-22 23:30:04 +03:00
2019-08-31 10:43:43 +03:00
down_read ( & frag - > frag_sem ) ;
if ( ! frag - > frag_dead ) {
/* result of ->release() is ignored */
buffer - > bin_attr - > write ( buffer - > item ,
buffer - > bin_buffer ,
buffer - > bin_buffer_size ) ;
}
up_read ( & frag - > frag_sem ) ;
2015-10-22 23:30:04 +03:00
}
2021-06-18 10:59:25 +03:00
vfree ( buffer - > bin_buffer ) ;
2019-08-30 18:30:03 +03:00
configfs_release ( inode , file ) ;
return 0 ;
2015-10-22 23:30:04 +03:00
}
2006-03-28 13:56:42 +04:00
const struct file_operations configfs_file_operations = {
2021-05-25 11:30:21 +03:00
. read_iter = configfs_read_iter ,
. write_iter = configfs_write_iter ,
2005-12-16 01:29:43 +03:00
. llseek = generic_file_llseek ,
. open = configfs_open_file ,
. release = configfs_release ,
} ;
2015-10-22 23:30:04 +03:00
const struct file_operations configfs_bin_file_operations = {
2021-05-25 11:30:21 +03:00
. read_iter = configfs_bin_read_iter ,
. write_iter = configfs_bin_write_iter ,
2015-10-22 23:30:04 +03:00
. llseek = NULL , /* bin file is not seekable */
. open = configfs_open_bin_file ,
. release = configfs_release_bin_file ,
} ;
2005-12-16 01:29:43 +03:00
/**
* configfs_create_file - create an attribute file for an item .
* @ item : item we ' re creating for .
* @ attr : atrribute descriptor .
*/
int configfs_create_file ( struct config_item * item , const struct configfs_attribute * attr )
{
2015-01-29 08:27:57 +03:00
struct dentry * dir = item - > ci_dentry ;
struct configfs_dirent * parent_sd = dir - > d_fsdata ;
umode_t mode = ( attr - > ca_mode & S_IALLUGO ) | S_IFREG ;
int error = 0 ;
2005-12-16 01:29:43 +03:00
2016-01-22 23:40:57 +03:00
inode_lock_nested ( d_inode ( dir ) , I_MUTEX_NORMAL ) ;
2015-01-29 08:27:57 +03:00
error = configfs_make_dirent ( parent_sd , NULL , ( void * ) attr , mode ,
2019-08-26 02:56:13 +03:00
CONFIGFS_ITEM_ATTR , parent_sd - > s_frag ) ;
2016-01-22 23:40:57 +03:00
inode_unlock ( d_inode ( dir ) ) ;
2015-01-29 08:27:57 +03:00
return error ;
2005-12-16 01:29:43 +03:00
}
2015-10-22 23:30:04 +03:00
/**
* configfs_create_bin_file - create a binary attribute file for an item .
* @ item : item we ' re creating for .
2021-05-25 11:24:23 +03:00
* @ bin_attr : atrribute descriptor .
2015-10-22 23:30:04 +03:00
*/
int configfs_create_bin_file ( struct config_item * item ,
const struct configfs_bin_attribute * bin_attr )
{
struct dentry * dir = item - > ci_dentry ;
struct configfs_dirent * parent_sd = dir - > d_fsdata ;
umode_t mode = ( bin_attr - > cb_attr . ca_mode & S_IALLUGO ) | S_IFREG ;
int error = 0 ;
2016-01-22 23:40:57 +03:00
inode_lock_nested ( dir - > d_inode , I_MUTEX_NORMAL ) ;
2015-10-22 23:30:04 +03:00
error = configfs_make_dirent ( parent_sd , NULL , ( void * ) bin_attr , mode ,
2019-08-26 02:56:13 +03:00
CONFIGFS_ITEM_BIN_ATTR , parent_sd - > s_frag ) ;
2016-01-22 23:40:57 +03:00
inode_unlock ( dir - > d_inode ) ;
2015-10-22 23:30:04 +03:00
return error ;
}