2021-03-16 04:49:09 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2016 Namjae Jeon < linkinjeon @ kernel . org >
* Copyright ( C ) 2018 Samsung Electronics Co . , Ltd .
*/
# include <linux/kernel.h>
# include <linux/xattr.h>
# include <linux/fs.h>
# include "misc.h"
# include "smb_common.h"
# include "connection.h"
# include "vfs.h"
# include "mgmt/share_config.h"
/**
* match_pattern ( ) - compare a string with a pattern which might include
* wildcard ' * ' and ' ? '
* TODO : implement consideration about DOS_DOT , DOS_QM and DOS_STAR
*
* @ string : string to compare with a pattern
2021-03-21 11:32:19 +03:00
* @ len : string length
2021-03-16 04:49:09 +03:00
* @ pattern : pattern string which might include wildcard ' * ' and ' ? '
*
* Return : 0 if pattern matched with the string , otherwise non zero value
*/
2021-03-21 11:32:19 +03:00
int match_pattern ( const char * str , size_t len , const char * pattern )
2021-03-16 04:49:09 +03:00
{
const char * s = str ;
const char * p = pattern ;
bool star = false ;
2021-03-21 11:32:19 +03:00
while ( * s & & len ) {
2021-03-16 04:49:09 +03:00
switch ( * p ) {
case ' ? ' :
s + + ;
2021-03-21 11:32:19 +03:00
len - - ;
2021-03-16 04:49:09 +03:00
p + + ;
break ;
case ' * ' :
star = true ;
str = s ;
if ( ! * + + p )
return true ;
pattern = p ;
break ;
default :
if ( tolower ( * s ) = = tolower ( * p ) ) {
s + + ;
2021-03-21 11:32:19 +03:00
len - - ;
2021-03-16 04:49:09 +03:00
p + + ;
} else {
if ( ! star )
return false ;
str + + ;
s = str ;
p = pattern ;
}
break ;
}
}
if ( * p = = ' * ' )
+ + p ;
return ! * p ;
}
/*
* is_char_allowed ( ) - check for valid character
* @ ch : input character to be checked
*
* Return : 1 if char is allowed , otherwise 0
*/
static inline int is_char_allowed ( char ch )
{
/* check for control chars, wildcards etc. */
if ( ! ( ch & 0x80 ) & &
2021-03-30 08:25:35 +03:00
( ch < = 0x1f | |
ch = = ' ? ' | | ch = = ' " ' | | ch = = ' < ' | |
ch = = ' > ' | | ch = = ' | ' | | ch = = ' * ' ) )
2021-03-16 04:49:09 +03:00
return 0 ;
return 1 ;
}
int ksmbd_validate_filename ( char * filename )
{
while ( * filename ) {
char c = * filename ;
filename + + ;
if ( ! is_char_allowed ( c ) ) {
ksmbd_debug ( VFS , " File name validation failed: 0x%x \n " , c ) ;
return - ENOENT ;
}
}
return 0 ;
}
static int ksmbd_validate_stream_name ( char * stream_name )
{
while ( * stream_name ) {
char c = * stream_name ;
stream_name + + ;
if ( c = = ' / ' | | c = = ' : ' | | c = = ' \\ ' ) {
ksmbd_err ( " Stream name validation failed: %c \n " , c ) ;
return - ENOENT ;
}
}
return 0 ;
}
int parse_stream_name ( char * filename , char * * stream_name , int * s_type )
{
char * stream_type ;
char * s_name ;
int rc = 0 ;
s_name = filename ;
filename = strsep ( & s_name , " : " ) ;
ksmbd_debug ( SMB , " filename : %s, streams : %s \n " , filename , s_name ) ;
if ( strchr ( s_name , ' : ' ) ) {
stream_type = s_name ;
s_name = strsep ( & stream_type , " : " ) ;
rc = ksmbd_validate_stream_name ( s_name ) ;
if ( rc < 0 ) {
rc = - ENOENT ;
goto out ;
}
ksmbd_debug ( SMB , " stream name : %s, stream type : %s \n " , s_name ,
stream_type ) ;
if ( ! strncasecmp ( " $data " , stream_type , 5 ) )
* s_type = DATA_STREAM ;
else if ( ! strncasecmp ( " $index_allocation " , stream_type , 17 ) )
* s_type = DIR_STREAM ;
else
rc = - ENOENT ;
}
* stream_name = s_name ;
out :
return rc ;
}
/**
* convert_to_nt_pathname ( ) - extract and return windows path string
* whose share directory prefix was removed from file path
* @ filename : unix filename
* @ sharepath : share path string
*
* Return : windows path string or error
*/
char * convert_to_nt_pathname ( char * filename , char * sharepath )
{
char * ab_pathname ;
int len , name_len ;
name_len = strlen ( filename ) ;
ab_pathname = kmalloc ( name_len , GFP_KERNEL ) ;
if ( ! ab_pathname )
return NULL ;
ab_pathname [ 0 ] = ' \\ ' ;
ab_pathname [ 1 ] = ' \0 ' ;
len = strlen ( sharepath ) ;
if ( ! strncmp ( filename , sharepath , len ) & & name_len ! = len ) {
strscpy ( ab_pathname , & filename [ len ] , name_len ) ;
ksmbd_conv_path_to_windows ( ab_pathname ) ;
}
return ab_pathname ;
}
int get_nlink ( struct kstat * st )
{
int nlink ;
nlink = st - > nlink ;
if ( S_ISDIR ( st - > mode ) )
nlink - - ;
return nlink ;
}
void ksmbd_conv_path_to_unix ( char * path )
{
strreplace ( path , ' \\ ' , ' / ' ) ;
}
void ksmbd_strip_last_slash ( char * path )
{
int len = strlen ( path ) ;
while ( len & & path [ len - 1 ] = = ' / ' ) {
path [ len - 1 ] = ' \0 ' ;
len - - ;
}
}
void ksmbd_conv_path_to_windows ( char * path )
{
strreplace ( path , ' / ' , ' \\ ' ) ;
}
/**
2021-03-17 11:01:15 +03:00
* ksmbd_extract_sharename ( ) - get share name from tree connect request
2021-03-16 04:49:09 +03:00
* @ treename : buffer containing tree name and share name
*
* Return : share name on success , otherwise error
*/
2021-03-17 11:01:15 +03:00
char * ksmbd_extract_sharename ( char * treename )
2021-03-16 04:49:09 +03:00
{
char * name = treename ;
char * dst ;
char * pos = strrchr ( name , ' \\ ' ) ;
if ( pos )
name = ( pos + 1 ) ;
/* caller has to free the memory */
dst = kstrdup ( name , GFP_KERNEL ) ;
if ( ! dst )
return ERR_PTR ( - ENOMEM ) ;
return dst ;
}
/**
* convert_to_unix_name ( ) - convert windows name to unix format
* @ path : name to be converted
* @ tid : tree id of mathing share
*
* Return : converted name on success , otherwise NULL
*/
char * convert_to_unix_name ( struct ksmbd_share_config * share , char * name )
{
int no_slash = 0 , name_len , path_len ;
char * new_name ;
if ( name [ 0 ] = = ' / ' )
name + + ;
path_len = share - > path_sz ;
name_len = strlen ( name ) ;
new_name = kmalloc ( path_len + name_len + 2 , GFP_KERNEL ) ;
if ( ! new_name )
return new_name ;
memcpy ( new_name , share - > path , path_len ) ;
if ( new_name [ path_len - 1 ] ! = ' / ' ) {
new_name [ path_len ] = ' / ' ;
no_slash = 1 ;
}
memcpy ( new_name + path_len + no_slash , name , name_len ) ;
path_len + = name_len + no_slash ;
new_name [ path_len ] = 0x00 ;
return new_name ;
}
char * ksmbd_convert_dir_info_name ( struct ksmbd_dir_info * d_info ,
2021-03-30 08:25:35 +03:00
const struct nls_table * local_nls , int * conv_len )
2021-03-16 04:49:09 +03:00
{
char * conv ;
int sz = min ( 4 * d_info - > name_len , PATH_MAX ) ;
if ( ! sz )
return NULL ;
conv = kmalloc ( sz , GFP_KERNEL ) ;
if ( ! conv )
return NULL ;
/* XXX */
* conv_len = smbConvertToUTF16 ( ( __le16 * ) conv ,
d_info - > name ,
d_info - > name_len ,
local_nls ,
0 ) ;
* conv_len * = 2 ;
/* We allocate buffer twice bigger than needed. */
conv [ * conv_len ] = 0x00 ;
conv [ * conv_len + 1 ] = 0x00 ;
return conv ;
}