diff options
-rw-r--r-- | source3/client/client.c | 18 | ||||
-rw-r--r-- | source3/libsmb/clifile.c | 44 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 9 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 95 |
4 files changed, 136 insertions, 30 deletions
diff --git a/source3/client/client.c b/source3/client/client.c index d0bc01655f..3a189a13e7 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -1773,13 +1773,11 @@ static int cmd_rename(void) return 0; } -#if 0 - This will become a hard link call. JRA. /**************************************************************************** - Rename some file using the NT call. + Hard link files using the NT call. ****************************************************************************/ -static int cmd_ntrename(void) +static int cmd_hardlink(void) { pstring src,dest; fstring buf,buf2; @@ -1789,21 +1787,20 @@ static int cmd_ntrename(void) if (!next_token_nr(NULL,buf,NULL,sizeof(buf)) || !next_token_nr(NULL,buf2,NULL, sizeof(buf2))) { - d_printf("ntrename <src> <dest>\n"); + d_printf("hardlink <src> <dest>\n"); return 1; } pstrcat(src,buf); pstrcat(dest,buf2); - if (!cli_ntrename(cli, src, dest)) { - d_printf("%s doing an NT rename of files\n",cli_errstr(cli)); + if (!cli_nt_hardlink(cli, src, dest)) { + d_printf("%s doing an NT hard link of files\n",cli_errstr(cli)); return 1; } return 0; } -#endif /**************************************************************************** Toggle the prompt flag. @@ -2191,6 +2188,7 @@ static struct {"du",cmd_du,"<mask> computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}}, {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}}, {"get",cmd_get,"<remote name> [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}}, + {"hardlink",cmd_hardlink,"<src> <dest> create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}}, {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}}, {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}}, {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}}, @@ -2204,10 +2202,6 @@ static struct {"more",cmd_more,"<remote name> view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}}, {"mput",cmd_mput,"<mask> put all matching files",{COMPL_REMOTE,COMPL_NONE}}, {"newer",cmd_newer,"<file> only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}}, -#if 0 - /* This call will eventually morph into a hard link call. JRA */ - {"ntrename",cmd_ntrename,"<src> <dest> NT rename some files",{COMPL_REMOTE,COMPL_REMOTE}}, -#endif {"open",cmd_open,"<mask> open a file",{COMPL_REMOTE,COMPL_NONE}}, {"print",cmd_print,"<file name> print a file",{COMPL_NONE,COMPL_NONE}}, {"printmode",cmd_printmode,"<graphics or text> set the print mode",{COMPL_NONE,COMPL_NONE}}, diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 9b766987ef..a3fa811e29 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -36,16 +36,18 @@ static BOOL cli_link_internal(struct cli_state *cli, const char *fname_src, cons pstring data; char *rparam=NULL, *rdata=NULL; char *p; + size_t srclen = 2*(strlen(fname_src)+1); + size_t destlen = 2*(strlen(fname_dst) + 1); memset(param, 0, sizeof(param)); SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK); p = ¶m[6]; - p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE); + p += clistr_push(cli, p, fname_src, MIN(srclen, sizeof(param)-6), STR_TERMINATE); param_len = PTR_DIFF(p, param); p = data; - p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE); + p += clistr_push(cli, p, fname_dst, MIN(destlen,sizeof(data)), STR_TERMINATE); data_len = PTR_DIFF(p, data); if (!cli_send_trans(cli, SMBtrans2, @@ -262,6 +264,44 @@ BOOL cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fnam } /**************************************************************************** + NT hardlink a file. +****************************************************************************/ + +BOOL cli_nt_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf, 4, 0, True); + + SCVAL(cli->outbuf,smb_com,SMBntrename); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR); + SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_HARD_LINK); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE); + *p++ = 4; + p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + if (cli_is_error(cli)) + return False; + + return True; +} + +/**************************************************************************** Delete a file. ****************************************************************************/ diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 9a0063481a..11518c24f7 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -1521,7 +1521,7 @@ int reply_ntrename(connection_struct *conn, START_PROFILE(SMBntrename); - if (rename_type != RENAME_FLAG_RENAME) { + if ((rename_type != RENAME_FLAG_RENAME) && (rename_type != RENAME_FLAG_HARD_LINK)) { END_PROFILE(SMBntrename); return ERROR_NT(NT_STATUS_ACCESS_DENIED); } @@ -1551,7 +1551,12 @@ int reply_ntrename(connection_struct *conn, DEBUG(3,("reply_ntrename : %s -> %s\n",name,newname)); - status = rename_internals(conn, name, newname, attrs, False); + if (rename_type == RENAME_FLAG_RENAME) { + status = rename_internals(conn, name, newname, attrs, False); + } else { + status = hardlink_internals(conn, name, newname); + } + if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBntrename); return ERROR_NT(status); diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 1d580e6f5b..e2df517f40 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -2478,6 +2478,82 @@ static int ensure_link_is_safe(connection_struct *conn, const char *link_dest_in } /**************************************************************************** + Set a hard link (called by UNIX extensions and by NT rename with HARD link + code. +****************************************************************************/ + +NTSTATUS hardlink_internals(connection_struct *conn, char *name, char *newname) +{ + BOOL bad_path_src = False; + BOOL bad_path_dest = False; + SMB_STRUCT_STAT sbuf1, sbuf2; + BOOL rc, rcdest; + pstring last_component_src; + pstring last_component_dest; + NTSTATUS status = NT_STATUS_OK; + + ZERO_STRUCT(sbuf1); + ZERO_STRUCT(sbuf2); + + /* No wildcards. */ + if (ms_has_wild(name) || ms_has_wild(newname)) { + return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; + } + + rc = unix_convert(name,conn,last_component_src,&bad_path_src,&sbuf1); + if (!rc && bad_path_src) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_src[0] == '.') { + if (!last_component_src[1] || (last_component_src[1] == '.' && !last_component_src[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + /* source must already exist. */ + if (!VALID_STAT(sbuf1)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + rcdest = unix_convert(newname,conn,last_component_dest,&bad_path_dest,&sbuf2); + if (!rcdest && bad_path_dest) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* Quick check for "." and ".." */ + if (last_component_dest[0] == '.') { + if (!last_component_dest[1] || (last_component_dest[1] == '.' && !last_component_dest[2])) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + } + + /* Disallow if already exists. */ + if (VALID_STAT(sbuf2)) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + /* No links from a directory. */ + if (S_ISDIR(sbuf1.st_mode)) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + + if (ensure_link_is_safe(conn, newname, newname) != 0) + return NT_STATUS_ACCESS_DENIED; + + DEBUG(10,("hardlink_internals: doing hard link %s -> %s\n", name, newname )); + + if (SMB_VFS_LINK(conn,name,newname) != 0) { + status = map_nt_error_from_unix(errno); + DEBUG(3,("hardlink_internals: Error %s link %s -> %s\n", + nt_errstr(status), name,newname)); + } + + return status; +} + +/**************************************************************************** Reply to a TRANS2_SETFILEINFO (set file info by fileid). ****************************************************************************/ @@ -2964,10 +3040,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (!lp_symlinks(SNUM(conn))) return(ERROR_DOS(ERRDOS,ERRnoaccess)); - /* Disallow if already exists. */ - if (VALID_STAT(sbuf)) - return(ERROR_DOS(ERRDOS,ERRbadpath)); - srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); @@ -2991,24 +3063,19 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", pstring link_dest; /* Set a hard link. */ - - /* Disallow if already exists. */ - if (VALID_STAT(sbuf)) - return(ERROR_DOS(ERRDOS,ERRbadpath)); - srvstr_get_path(inbuf, link_dest, pdata, sizeof(link_dest), -1, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { return ERROR_NT(status); } - if (ensure_link_is_safe(conn, link_dest, link_dest) != 0) - return(UNIXERROR(ERRDOS,ERRnoaccess)); - DEBUG(10,("call_trans2setfilepathinfo: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n", fname, link_dest )); - if (SMB_VFS_LINK(conn,link_dest,fname) != 0) - return(UNIXERROR(ERRDOS,ERRnoaccess)); + status = hardlink_internals(conn, fname, link_dest); + if (!NT_STATUS_IS_OK(status)) { + return ERROR_NT(status); + } + SSVAL(params,0,0); send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); return(-1); |