X-Git-Url: http://git.samba.org/metze/?a=blobdiff_plain;f=source3%2Fsmbd%2Freply.c;h=7c72216b0215c594b2321713c897f7ece4183999;hb=acfeaa411958ac4ee43d45d47802da74fea14eef;hp=b8be3ed304544422c9de271bdaf390b4667b0fe6;hpb=9617a3945b804bf2d1409285df2a7add12690063;p=metze%2Fsamba%2Fwip.git diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index b8be3ed30454..7c72216b0215 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -25,13 +25,9 @@ */ #include "includes.h" +#include "smbd/globals.h" -/* look in server.c for some explanation of these variables */ extern enum protocol_types Protocol; -extern int max_recv; -extern uint32 global_client_caps; - -extern bool global_encrypted_passwords_negotiated; /**************************************************************************** Ensure we check the path in *exactly* the same way as W2K for a findfirst/findnext @@ -76,11 +72,16 @@ static NTSTATUS check_path_syntax_internal(char *path, } } - if (!stream_started && *s == ':') { + if (!posix_path && !stream_started && *s == ':') { if (*p_last_component_contains_wcard) { return NT_STATUS_OBJECT_NAME_INVALID; } - /* stream names allow more characters than file names */ + /* Stream names allow more characters than file names. + We're overloading posix_path here to allow a wider + range of characters. If stream_started is true this + is still a Windows path even if posix_path is true. + JRA. + */ stream_started = true; start_of_name_component = false; posix_path = true; @@ -406,6 +407,95 @@ bool fsp_belongs_conn(connection_struct *conn, struct smb_request *req, return False; } +static bool netbios_session_retarget(const char *name, int name_type) +{ + char *trim_name; + char *trim_name_type; + const char *retarget_parm; + char *retarget; + char *p; + int retarget_type = 0x20; + int retarget_port = 139; + struct sockaddr_storage retarget_addr; + struct sockaddr_in *in_addr; + bool ret = false; + uint8_t outbuf[10]; + + if (get_socket_port(smbd_server_fd()) != 139) { + return false; + } + + trim_name = talloc_strdup(talloc_tos(), name); + if (trim_name == NULL) { + goto fail; + } + trim_char(trim_name, ' ', ' '); + + trim_name_type = talloc_asprintf(trim_name, "%s#%2.2x", trim_name, + name_type); + if (trim_name_type == NULL) { + goto fail; + } + + retarget_parm = lp_parm_const_string(-1, "netbios retarget", + trim_name_type, NULL); + if (retarget_parm == NULL) { + retarget_parm = lp_parm_const_string(-1, "netbios retarget", + trim_name, NULL); + } + if (retarget_parm == NULL) { + goto fail; + } + + retarget = talloc_strdup(trim_name, retarget_parm); + if (retarget == NULL) { + goto fail; + } + + DEBUG(10, ("retargeting %s to %s\n", trim_name_type, retarget)); + + p = strchr(retarget, ':'); + if (p != NULL) { + *p++ = '\0'; + retarget_port = atoi(p); + } + + p = strchr_m(retarget, '#'); + if (p != NULL) { + *p++ = '\0'; + sscanf(p, "%x", &retarget_type); + } + + ret = resolve_name(retarget, &retarget_addr, retarget_type); + if (!ret) { + DEBUG(10, ("could not resolve %s\n", retarget)); + goto fail; + } + + if (retarget_addr.ss_family != AF_INET) { + DEBUG(10, ("Retarget target not an IPv4 addr\n")); + goto fail; + } + + in_addr = (struct sockaddr_in *)(void *)&retarget_addr; + + _smb_setlen(outbuf, 6); + SCVAL(outbuf, 0, 0x84); + *(uint32_t *)(outbuf+4) = in_addr->sin_addr.s_addr; + *(uint16_t *)(outbuf+8) = htons(retarget_port); + + if (!srv_send_smb(smbd_server_fd(), (char *)outbuf, false, 0, false, + NULL)) { + exit_server_cleanly("netbios_session_regarget: srv_send_smb " + "failed."); + } + + ret = true; + fail: + TALLOC_FREE(trim_name); + return ret; +} + /**************************************************************************** Reply to a (netbios-level) special message. ****************************************************************************/ @@ -415,7 +505,8 @@ void reply_special(char *inbuf) int msg_type = CVAL(inbuf,0); int msg_flags = CVAL(inbuf,1); fstring name1,name2; - char name_type = 0; + char name_type1, name_type2; + struct smbd_server_connection *sconn = smbd_server_conn; /* * We only really use 4 bytes of the outbuf, but for the smb_setlen @@ -424,8 +515,6 @@ void reply_special(char *inbuf) */ char outbuf[smb_size]; - static bool already_got_session = False; - *name1 = *name2 = 0; memset(outbuf, '\0', sizeof(outbuf)); @@ -435,7 +524,7 @@ void reply_special(char *inbuf) switch (msg_type) { case 0x81: /* session request */ - if (already_got_session) { + if (sconn->nbt.got_session) { exit_server_cleanly("multiple session request not permitted"); } @@ -446,19 +535,23 @@ void reply_special(char *inbuf) DEBUG(0,("Invalid name length in session request\n")); return; } - name_extract(inbuf,4,name1); - name_type = name_extract(inbuf,4 + name_len(inbuf + 4),name2); - DEBUG(2,("netbios connect: name1=%s name2=%s\n", - name1,name2)); + name_type1 = name_extract(inbuf,4,name1); + name_type2 = name_extract(inbuf,4 + name_len(inbuf + 4),name2); + DEBUG(2,("netbios connect: name1=%s0x%x name2=%s0x%x\n", + name1, name_type1, name2, name_type2)); + + if (netbios_session_retarget(name1, name_type1)) { + exit_server_cleanly("retargeted client"); + } set_local_machine_name(name1, True); set_remote_machine_name(name2, True); DEBUG(2,("netbios connect: local=%s remote=%s, name type = %x\n", get_local_machine_name(), get_remote_machine_name(), - name_type)); + name_type2)); - if (name_type == 'R') { + if (name_type2 == 'R') { /* We are being asked for a pathworks session --- no thanks! */ SCVAL(outbuf, 0,0x83); @@ -469,13 +562,13 @@ void reply_special(char *inbuf) of possibly valid usernames if we are operating in share mode security */ if (lp_security() == SEC_SHARE) { - add_session_user(get_remote_machine_name()); + add_session_user(sconn, get_remote_machine_name()); } reload_services(True); reopen_logs(); - already_got_session = True; + sconn->nbt.got_session = true; break; case 0x89: /* session keepalive request @@ -498,7 +591,7 @@ void reply_special(char *inbuf) DEBUG(5,("init msg_type=0x%x msg_flags=0x%x\n", msg_type, msg_flags)); - srv_send_smb(smbd_server_fd(), outbuf, false); + srv_send_smb(smbd_server_fd(), outbuf, false, 0, false, NULL); return; } @@ -519,6 +612,7 @@ void reply_tcon(struct smb_request *req) const char *p; DATA_BLOB password_blob; TALLOC_CTX *ctx = talloc_tos(); + struct smbd_server_connection *sconn = smbd_server_conn; START_PROFILE(SMBtcon); @@ -550,7 +644,8 @@ void reply_tcon(struct smb_request *req) password_blob = data_blob(password, pwlen+1); - conn = make_connection(service,password_blob,dev,req->vuid,&nt_status); + conn = make_connection(sconn,service,password_blob,dev, + req->vuid,&nt_status); req->conn = conn; data_blob_clear_free(&password_blob); @@ -562,7 +657,7 @@ void reply_tcon(struct smb_request *req) } reply_outbuf(req, 2, 0); - SSVAL(req->outbuf,smb_vwv0,max_recv); + SSVAL(req->outbuf,smb_vwv0,sconn->smb1.negprot.max_recv); SSVAL(req->outbuf,smb_vwv1,conn->cnum); SSVAL(req->outbuf,smb_tid,conn->cnum); @@ -593,6 +688,7 @@ void reply_tcon_and_X(struct smb_request *req) char *path = NULL; const char *p, *q; uint16 tcon_flags; + struct smbd_server_connection *sconn = smbd_server_conn; START_PROFILE(SMBtconX); @@ -607,7 +703,7 @@ void reply_tcon_and_X(struct smb_request *req) /* we might have to close an old one */ if ((tcon_flags & 0x1) && conn) { - close_cnum(conn,req->vuid); + close_cnum(sconn, conn,req->vuid); req->conn = NULL; conn = NULL; } @@ -618,7 +714,7 @@ void reply_tcon_and_X(struct smb_request *req) return; } - if (global_encrypted_passwords_negotiated) { + if (sconn->smb1.negprot.encrypted_passwords) { password = data_blob_talloc(talloc_tos(), req->buf, passlen); if (lp_security() == SEC_SHARE) { /* @@ -675,7 +771,7 @@ void reply_tcon_and_X(struct smb_request *req) DEBUG(4,("Client requested device type [%s] for share [%s]\n", client_devicetype, service)); - conn = make_connection(service, password, client_devicetype, + conn = make_connection(sconn, service, password, client_devicetype, req->vuid, &nt_status); req->conn =conn; @@ -874,8 +970,8 @@ static NTSTATUS map_checkpath_error(uint16_t flags2, NTSTATUS status) void reply_checkpath(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *name = NULL; - SMB_STRUCT_STAT sbuf; NTSTATUS status; TALLOC_CTX *ctx = talloc_tos(); @@ -891,10 +987,15 @@ void reply_checkpath(struct smb_request *req) return; } - status = resolve_dfspath(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - name, - &name); + DEBUG(3,("reply_checkpath %s mode=%d\n", name, (int)SVAL(req->vwv+0, 0))); + + status = filename_convert(ctx, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + name, + &smb_fname, + NULL); + if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, @@ -905,41 +1006,23 @@ void reply_checkpath(struct smb_request *req) goto path_err; } - DEBUG(3,("reply_checkpath %s mode=%d\n", name, (int)SVAL(req->vwv+0, 0))); - - status = unix_convert(ctx, conn, name, False, &name, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - goto path_err; - } - - status = check_name(conn, name); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3,("reply_checkpath: check_name of %s failed (%s)\n",name,nt_errstr(status))); - goto path_err; - } - - if (!VALID_STAT(sbuf) && (SMB_VFS_STAT(conn,name,&sbuf) != 0)) { - DEBUG(3,("reply_checkpath: stat of %s failed (%s)\n",name,strerror(errno))); + if (!VALID_STAT(smb_fname->st) && + (SMB_VFS_STAT(conn, smb_fname) != 0)) { + DEBUG(3,("reply_checkpath: stat of %s failed (%s)\n", + smb_fname_str_dbg(smb_fname), strerror(errno))); status = map_nt_error_from_unix(errno); goto path_err; } - if (!S_ISDIR(sbuf.st_mode)) { + if (!S_ISDIR(smb_fname->st.st_ex_mode)) { reply_botherror(req, NT_STATUS_NOT_A_DIRECTORY, ERRDOS, ERRbadpath); - END_PROFILE(SMBcheckpath); - return; + goto out; } reply_outbuf(req, 0, 0); - END_PROFILE(SMBcheckpath); - return; - - path_err: - - END_PROFILE(SMBcheckpath); - + path_err: /* We special case this - as when a Windows machine is parsing a path is steps through the components one at a time - if a component fails it expects @@ -956,10 +1039,15 @@ void reply_checkpath(struct smb_request *req) */ reply_botherror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERRbadpath); - return; + goto out; } reply_nterror(req, status); + + out: + TALLOC_FREE(smb_fname); + END_PROFILE(SMBcheckpath); + return; } /**************************************************************************** @@ -969,8 +1057,8 @@ void reply_checkpath(struct smb_request *req) void reply_getatr(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; - SMB_STRUCT_STAT sbuf; int mode=0; SMB_OFF_T size=0; time_t mtime=0; @@ -984,24 +1072,7 @@ void reply_getatr(struct smb_request *req) p += srvstr_get_path_req(ctx, req, &fname, p, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBgetatr); - return; - } - - status = resolve_dfspath(ctx, conn, - req->flags2 & FLAGS2_DFS_PATHNAMES, - fname, - &fname); - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { - reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, - ERRSRV, ERRbadpath); - END_PROFILE(SMBgetatr); - return; - } - reply_nterror(req, status); - END_PROFILE(SMBgetatr); - return; + goto out; } /* dos smetimes asks for a stat of "" - it returns a "hidden directory" @@ -1014,29 +1085,33 @@ void reply_getatr(struct smb_request *req) size = 0; mtime = 0; } else { - status = unix_convert(ctx, conn, fname, False, &fname, NULL,&sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBgetatr); - return; - } - status = check_name(conn, fname); + status = filename_convert(ctx, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + fname, + &smb_fname, + &fname); if (!NT_STATUS_IS_OK(status)) { - DEBUG(3,("reply_getatr: check_name of %s failed (%s)\n",fname,nt_errstr(status))); + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } reply_nterror(req, status); - END_PROFILE(SMBgetatr); - return; + goto out; } - if (!VALID_STAT(sbuf) && (SMB_VFS_STAT(conn,fname,&sbuf) != 0)) { - DEBUG(3,("reply_getatr: stat of %s failed (%s)\n",fname,strerror(errno))); + if (!VALID_STAT(smb_fname->st) && + (SMB_VFS_STAT(conn, smb_fname) != 0)) { + DEBUG(3,("reply_getatr: stat of %s failed (%s)\n", + smb_fname_str_dbg(smb_fname), + strerror(errno))); reply_unixerror(req, ERRDOS,ERRbadfile); - END_PROFILE(SMBgetatr); - return; + goto out; } - mode = dos_mode(conn,fname,&sbuf); - size = sbuf.st_size; - mtime = sbuf.st_mtime; + mode = dos_mode(conn, fname, &smb_fname->st); + size = smb_fname->st.st_ex_size; + mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); if (mode & aDIR) { size = 0; } @@ -1057,8 +1132,12 @@ void reply_getatr(struct smb_request *req) SVAL(req->outbuf, smb_flg2) | FLAGS2_IS_LONG_NAME); } - DEBUG(3,("reply_getatr: name=%s mode=%d size=%u\n", fname, mode, (unsigned int)size ) ); + DEBUG(3,("reply_getatr: name=%s mode=%d size=%u\n", + smb_fname_str_dbg(smb_fname), mode, (unsigned int)size)); + out: + TALLOC_FREE(smb_fname); + TALLOC_FREE(fname); END_PROFILE(SMBgetatr); return; } @@ -1069,102 +1148,87 @@ void reply_getatr(struct smb_request *req) void reply_setatr(struct smb_request *req) { - struct timespec ts[2]; + struct smb_file_time ft; connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; int mode; time_t mtime; - SMB_STRUCT_STAT sbuf; const char *p; NTSTATUS status; TALLOC_CTX *ctx = talloc_tos(); START_PROFILE(SMBsetatr); - ZERO_STRUCT(ts); + ZERO_STRUCT(ft); if (req->wct < 2) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + goto out; } p = (const char *)req->buf + 1; p += srvstr_get_path_req(ctx, req, &fname, p, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBsetatr); - return; + goto out; } - status = resolve_dfspath(ctx, conn, + status = filename_convert(ctx, + conn, req->flags2 & FLAGS2_DFS_PATHNAMES, fname, - &fname); + &smb_fname, + NULL); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBsetatr); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBsetatr); - return; - } - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBsetatr); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBsetatr); - return; + goto out; } - if (fname[0] == '.' && fname[1] == '\0') { + if (smb_fname->base_name[0] == '.' && + smb_fname->base_name[1] == '\0') { /* * Not sure here is the right place to catch this * condition. Might be moved to somewhere else later -- vl */ reply_nterror(req, NT_STATUS_ACCESS_DENIED); - END_PROFILE(SMBsetatr); - return; + goto out; } mode = SVAL(req->vwv+0, 0); mtime = srv_make_unix_date3(req->vwv+1); - ts[1] = convert_time_t_to_timespec(mtime); - status = smb_set_file_time(conn, NULL, fname, - &sbuf, ts, true); + ft.mtime = convert_time_t_to_timespec(mtime); + status = smb_set_file_time(conn, NULL, smb_fname, &ft, true); if (!NT_STATUS_IS_OK(status)) { reply_unixerror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBsetatr); - return; + goto out; } if (mode != FILE_ATTRIBUTE_NORMAL) { - if (VALID_STAT_OF_DIR(sbuf)) + if (VALID_STAT_OF_DIR(smb_fname->st)) mode |= aDIR; else mode &= ~aDIR; - if (file_set_dosmode(conn,fname,mode,&sbuf,NULL,false) != 0) { + if (file_set_dosmode(conn, smb_fname, mode, NULL, + false) != 0) { reply_unixerror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBsetatr); - return; + goto out; } } reply_outbuf(req, 0, 0); - DEBUG( 3, ( "setatr name=%s mode=%d\n", fname, mode ) ); - + DEBUG(3, ("setatr name=%s mode=%d\n", smb_fname_str_dbg(smb_fname), + mode)); + out: + TALLOC_FREE(smb_fname); END_PROFILE(SMBsetatr); return; } @@ -1221,6 +1285,38 @@ void reply_dskattr(struct smb_request *req) return; } +/* + * Utility function to split the filename from the directory. + */ +static NTSTATUS split_fname_dir_mask(TALLOC_CTX *ctx, const char *fname_in, + char **fname_dir_out, + char **fname_mask_out) +{ + const char *p = NULL; + char *fname_dir = NULL; + char *fname_mask = NULL; + + p = strrchr_m(fname_in, '/'); + if (!p) { + fname_dir = talloc_strdup(ctx, "."); + fname_mask = talloc_strdup(ctx, fname_in); + } else { + fname_dir = talloc_strndup(ctx, fname_in, + PTR_DIFF(p, fname_in)); + fname_mask = talloc_strdup(ctx, p+1); + } + + if (!fname_dir || !fname_mask) { + TALLOC_FREE(fname_dir); + TALLOC_FREE(fname_mask); + return NT_STATUS_NO_MEMORY; + } + + *fname_dir_out = fname_dir; + *fname_mask_out = fname_mask; + return NT_STATUS_OK; +} + /**************************************************************************** Reply to a search. Can be called from SMBsearch, SMBffirst or SMBfunique. @@ -1229,19 +1325,19 @@ void reply_dskattr(struct smb_request *req) void reply_search(struct smb_request *req) { connection_struct *conn = req->conn; + char *path = NULL; const char *mask = NULL; char *directory = NULL; char *fname = NULL; SMB_OFF_T size; uint32 mode; - time_t date; + struct timespec date; uint32 dirtype; unsigned int numentries = 0; unsigned int maxentries = 0; bool finished = False; const char *p; int status_len; - char *path = NULL; char status[21]; int dptr_num= -1; bool check_descend = False; @@ -1283,34 +1379,42 @@ void reply_search(struct smb_request *req) return; } - nt_status = resolve_dfspath_wcard(ctx, conn, + p++; + status_len = SVAL(p, 0); + p += 2; + + /* dirtype &= ~aDIR; */ + + if (status_len == 0) { + struct smb_filename *smb_fname = NULL; + + nt_status = resolve_dfspath_wcard(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, path, &path, &mask_contains_wcard); - if (!NT_STATUS_IS_OK(nt_status)) { - if (NT_STATUS_EQUAL(nt_status,NT_STATUS_PATH_NOT_COVERED)) { - reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, - ERRSRV, ERRbadpath); + if (!NT_STATUS_IS_OK(nt_status)) { + if (NT_STATUS_EQUAL(nt_status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + END_PROFILE(SMBsearch); + return; + } + reply_nterror(req, nt_status); END_PROFILE(SMBsearch); return; } - reply_nterror(req, nt_status); - END_PROFILE(SMBsearch); - return; - } - - p++; - status_len = SVAL(p, 0); - p += 2; - - /* dirtype &= ~aDIR; */ - if (status_len == 0) { - SMB_STRUCT_STAT sbuf; + nt_status = unix_convert(ctx, conn, path, &smb_fname, + UCF_ALLOW_WCARD_LCOMP); + if (!NT_STATUS_IS_OK(nt_status)) { + reply_nterror(req, nt_status); + END_PROFILE(SMBsearch); + return; + } - nt_status = unix_convert(ctx, conn, path, True, - &directory, NULL, &sbuf); + nt_status = get_full_smb_filename(ctx, smb_fname, &directory); + TALLOC_FREE(smb_fname); if (!NT_STATUS_IS_OK(nt_status)) { reply_nterror(req, nt_status); END_PROFILE(SMBsearch); @@ -1386,6 +1490,9 @@ void reply_search(struct smb_request *req) DEBUG(4,("dptr_num is %d\n",dptr_num)); + /* Initialize per SMBsearch/SMBffirst/SMBfunique operation data */ + dptr_init_search_op(conn->dirptr); + if ((dirtype&0x1F) == aVOLID) { char buf[DIR_STRUCT_SIZE]; memcpy(buf,status,21); @@ -1442,7 +1549,7 @@ void reply_search(struct smb_request *req) fname, size, mode, - date, + convert_timespec_to_time_t(date), !allow_long_path_components)) { reply_nterror(req, NT_STATUS_NO_MEMORY); END_PROFILE(SMBsearch); @@ -1585,12 +1692,12 @@ void reply_fclose(struct smb_request *req) void reply_open(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; uint32 fattr=0; SMB_OFF_T size = 0; time_t mtime=0; int info; - SMB_STRUCT_STAT sbuf; files_struct *fsp; int oplock_request; int deny_mode; @@ -1606,8 +1713,7 @@ void reply_open(struct smb_request *req) if (req->wct < 2) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBopen); - return; + goto out; } oplock_request = CORE_OPLOCK_REQUEST(req->inbuf); @@ -1618,24 +1724,38 @@ void reply_open(struct smb_request *req) STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBopen); - return; + goto out; + } + + status = filename_convert(ctx, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + fname, + &smb_fname, + &fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } + reply_nterror(req, status); + goto out; } if (!map_open_params_to_ntcreate( fname, deny_mode, OPENX_FILE_EXISTS_OPEN, &access_mask, &share_mode, &create_disposition, &create_options)) { reply_nterror(req, NT_STATUS_DOS(ERRDOS, ERRbadaccess)); - END_PROFILE(SMBopen); - return; + goto out; } status = SMB_VFS_CREATE_FILE( conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - CFF_DOS_PATH, /* create_file_flags */ + smb_fname, /* fname */ access_mask, /* access_mask */ share_mode, /* share_access */ create_disposition, /* create_disposition*/ @@ -1646,30 +1766,26 @@ void reply_open(struct smb_request *req) NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - &info, /* pinfo */ - &sbuf); /* psbuf */ + &info); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { if (open_was_deferred(req->mid)) { /* We have re-scheduled this call. */ - END_PROFILE(SMBopen); - return; + goto out; } reply_openerror(req, status); - END_PROFILE(SMBopen); - return; + goto out; } - size = sbuf.st_size; - fattr = dos_mode(conn,fsp->fsp_name,&sbuf); - mtime = sbuf.st_mtime; + size = smb_fname->st.st_ex_size; + fattr = dos_mode(conn,fsp->fsp_name,&smb_fname->st); + mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); if (fattr & aDIR) { DEBUG(3,("attempt to open a directory %s\n",fsp->fsp_name)); close_file(req, fsp, ERROR_CLOSE); reply_doserror(req, ERRDOS,ERRnoaccess); - END_PROFILE(SMBopen); - return; + goto out; } reply_outbuf(req, 7, 0); @@ -1692,6 +1808,8 @@ void reply_open(struct smb_request *req) SCVAL(req->outbuf,smb_flg, CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED); } + out: + TALLOC_FREE(smb_fname); END_PROFILE(SMBopen); return; } @@ -1703,6 +1821,7 @@ void reply_open(struct smb_request *req) void reply_open_and_X(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; uint16 open_flags; int deny_mode; @@ -1719,7 +1838,6 @@ void reply_open_and_X(struct smb_request *req) int smb_ofun; uint32 fattr=0; int mtime=0; - SMB_STRUCT_STAT sbuf; int smb_action = 0; files_struct *fsp; NTSTATUS status; @@ -1735,8 +1853,7 @@ void reply_open_and_X(struct smb_request *req) if (req->wct < 15) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBopenX); - return; + goto out; } open_flags = SVAL(req->vwv+2, 0); @@ -1755,8 +1872,7 @@ void reply_open_and_X(struct smb_request *req) } else { reply_doserror(req, ERRSRV, ERRaccess); } - END_PROFILE(SMBopenX); - return; + goto out; } /* XXXX we need to handle passed times, sattr and flags */ @@ -1764,24 +1880,38 @@ void reply_open_and_X(struct smb_request *req) STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBopenX); - return; + goto out; + } + + status = filename_convert(ctx, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + fname, + &smb_fname, + &fname); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } + reply_nterror(req, status); + goto out; } if (!map_open_params_to_ntcreate( fname, deny_mode, smb_ofun, &access_mask, &share_mode, &create_disposition, &create_options)) { reply_nterror(req, NT_STATUS_DOS(ERRDOS, ERRbadaccess)); - END_PROFILE(SMBopenX); - return; + goto out; } status = SMB_VFS_CREATE_FILE( conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - CFF_DOS_PATH, /* create_file_flags */ + smb_fname, /* fname */ access_mask, /* access_mask */ share_mode, /* share_access */ create_disposition, /* create_disposition*/ @@ -1792,17 +1922,15 @@ void reply_open_and_X(struct smb_request *req) NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - &smb_action, /* pinfo */ - &sbuf); /* psbuf */ + &smb_action); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBopenX); if (open_was_deferred(req->mid)) { /* We have re-scheduled this call. */ - return; + goto out; } reply_openerror(req, status); - return; + goto out; } /* Setting the "size" field in vwv9 and vwv10 causes the file to be set to this size, @@ -1812,26 +1940,24 @@ void reply_open_and_X(struct smb_request *req) if (vfs_allocate_file_space(fsp, fsp->initial_allocation_size) == -1) { close_file(req, fsp, ERROR_CLOSE); reply_nterror(req, NT_STATUS_DISK_FULL); - END_PROFILE(SMBopenX); - return; + goto out; } retval = vfs_set_filelen(fsp, (SMB_OFF_T)allocation_size); if (retval < 0) { close_file(req, fsp, ERROR_CLOSE); reply_nterror(req, NT_STATUS_DISK_FULL); - END_PROFILE(SMBopenX); - return; + goto out; } - sbuf.st_size = get_allocation_size(conn,fsp,&sbuf); + smb_fname->st.st_ex_size = + SMB_VFS_GET_ALLOC_SIZE(conn, fsp, &smb_fname->st); } - fattr = dos_mode(conn,fsp->fsp_name,&sbuf); - mtime = sbuf.st_mtime; + fattr = dos_mode(conn,fsp->fsp_name,&smb_fname->st); + mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); if (fattr & aDIR) { close_file(req, fsp, ERROR_CLOSE); reply_doserror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBopenX); - return; + goto out; } /* If the caller set the extended oplock request bit @@ -1875,7 +2001,7 @@ void reply_open_and_X(struct smb_request *req) } else { srv_put_dos_date3((char *)req->outbuf,smb_vwv4,mtime); } - SIVAL(req->outbuf,smb_vwv6,(uint32)sbuf.st_size); + SIVAL(req->outbuf,smb_vwv6,(uint32)smb_fname->st.st_ex_size); SSVAL(req->outbuf,smb_vwv8,GET_OPENX_MODE(deny_mode)); SSVAL(req->outbuf,smb_vwv11,smb_action); @@ -1883,8 +2009,10 @@ void reply_open_and_X(struct smb_request *req) SIVAL(req->outbuf, smb_vwv15, STD_RIGHT_ALL_ACCESS); } - END_PROFILE(SMBopenX); chain_reply(req); + out: + TALLOC_FREE(smb_fname); + END_PROFILE(SMBopenX); return; } @@ -1894,11 +2022,12 @@ void reply_open_and_X(struct smb_request *req) void reply_ulogoffX(struct smb_request *req) { + struct smbd_server_connection *sconn = smbd_server_conn; user_struct *vuser; START_PROFILE(SMBulogoffX); - vuser = get_valid_user_struct(req->vuid); + vuser = get_valid_user_struct(sconn, req->vuid); if(vuser == NULL) { DEBUG(3,("ulogoff, vuser id %d does not map to user.\n", @@ -1911,7 +2040,7 @@ void reply_ulogoffX(struct smb_request *req) file_close_user(req->vuid); } - invalidate_vuid(req->vuid); + invalidate_vuid(sconn, req->vuid); reply_outbuf(req, 2, 0); @@ -1928,12 +2057,12 @@ void reply_ulogoffX(struct smb_request *req) void reply_mknew(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; uint32 fattr = 0; - struct timespec ts[2]; + struct smb_file_time ft; files_struct *fsp; int oplock_request = 0; - SMB_STRUCT_STAT sbuf; NTSTATUS status; uint32 access_mask = FILE_GENERIC_READ | FILE_GENERIC_WRITE; uint32 share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; @@ -1942,30 +2071,47 @@ void reply_mknew(struct smb_request *req) TALLOC_CTX *ctx = talloc_tos(); START_PROFILE(SMBcreate); + ZERO_STRUCT(ft); if (req->wct < 3) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBcreate); - return; + goto out; } fattr = SVAL(req->vwv+0, 0); oplock_request = CORE_OPLOCK_REQUEST(req->inbuf); - ts[1] = convert_time_t_to_timespec(srv_make_unix_date3(req->vwv+1)); - /* mtime. */ + /* mtime. */ + ft.mtime = convert_time_t_to_timespec(srv_make_unix_date3(req->vwv+1)); srvstr_get_path_req(ctx, req, &fname, (const char *)req->buf + 1, STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcreate); - return; + goto out; + } + + status = filename_convert(ctx, + conn, + req->flags2 & FLAGS2_DFS_PATHNAMES, + fname, + &smb_fname, + NULL); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { + reply_botherror(req, + NT_STATUS_PATH_NOT_COVERED, + ERRSRV, ERRbadpath); + goto out; + } + reply_nterror(req, status); + goto out; } if (fattr & aVOLID) { DEBUG(0,("Attempt to create file (%s) with volid set - " - "please report this\n", fname)); + "please report this\n", + smb_fname_str_dbg(smb_fname))); } if(req->cmd == SMBmknew) { @@ -1980,8 +2126,7 @@ void reply_mknew(struct smb_request *req) conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - CFF_DOS_PATH, /* create_file_flags */ + smb_fname, /* fname */ access_mask, /* access_mask */ share_mode, /* share_access */ create_disposition, /* create_disposition*/ @@ -1992,25 +2137,22 @@ void reply_mknew(struct smb_request *req) NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - &sbuf); /* psbuf */ + NULL); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { - END_PROFILE(SMBcreate); if (open_was_deferred(req->mid)) { /* We have re-scheduled this call. */ - return; + goto out; } reply_openerror(req, status); - return; + goto out; } - ts[0] = get_atimespec(&sbuf); /* atime. */ - status = smb_set_file_time(conn, fsp, fsp->fsp_name, &sbuf, ts, true); + ft.atime = smb_fname->st.st_ex_atime; /* atime. */ + status = smb_set_file_time(conn, fsp, smb_fname, &ft, true); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBcreate); - reply_openerror(req, status); - return; + goto out; } reply_outbuf(req, 1, 0); @@ -2026,10 +2168,13 @@ void reply_mknew(struct smb_request *req) CVAL(req->outbuf,smb_flg)|CORE_OPLOCK_GRANTED); } - DEBUG( 2, ( "reply_mknew: file %s\n", fsp->fsp_name ) ); - DEBUG( 3, ( "reply_mknew %s fd=%d dmode=0x%x\n", - fsp->fsp_name, fsp->fh->fd, (unsigned int)fattr ) ); + DEBUG(2, ("reply_mknew: file %s\n", smb_fname_str_dbg(smb_fname))); + DEBUG(3, ("reply_mknew %s fd=%d dmode=0x%x\n", + smb_fname_str_dbg(smb_fname), fsp->fh->fd, + (unsigned int)fattr)); + out: + TALLOC_FREE(smb_fname); END_PROFILE(SMBcreate); return; } @@ -2041,12 +2186,12 @@ void reply_mknew(struct smb_request *req) void reply_ctemp(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_fname = NULL; char *fname = NULL; uint32 fattr; files_struct *fsp; int oplock_request; int tmpfd; - SMB_STRUCT_STAT sbuf; char *s; NTSTATUS status; TALLOC_CTX *ctx = talloc_tos(); @@ -2055,8 +2200,7 @@ void reply_ctemp(struct smb_request *req) if (req->wct < 3) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBctemp); - return; + goto out; } fattr = SVAL(req->vwv+0, 0); @@ -2066,8 +2210,7 @@ void reply_ctemp(struct smb_request *req) STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBctemp); - return; + goto out; } if (*fname) { fname = talloc_asprintf(ctx, @@ -2079,56 +2222,38 @@ void reply_ctemp(struct smb_request *req) if (!fname) { reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBctemp); - return; + goto out; } - status = resolve_dfspath(ctx, conn, + status = filename_convert(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, fname, - &fname); + &smb_fname, + NULL); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBctemp); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBctemp); - return; - } - - status = unix_convert(ctx, conn, fname, False, &fname, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBctemp); - return; - } - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBctemp); - return; + goto out; } - tmpfd = smb_mkstemp(fname); + tmpfd = mkstemp(smb_fname->base_name); if (tmpfd == -1) { reply_unixerror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBctemp); - return; + goto out; } - SMB_VFS_STAT(conn,fname,&sbuf); + SMB_VFS_STAT(conn, smb_fname); /* We should fail if file does not exist. */ status = SMB_VFS_CREATE_FILE( conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - 0, /* create_file_flags */ + smb_fname, /* fname */ FILE_GENERIC_READ | FILE_GENERIC_WRITE, /* access_mask */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ FILE_OPEN, /* create_disposition*/ @@ -2139,21 +2264,18 @@ void reply_ctemp(struct smb_request *req) NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - &sbuf); /* psbuf */ + NULL); /* pinfo */ - /* close fd from smb_mkstemp() */ + /* close fd from mkstemp() */ close(tmpfd); if (!NT_STATUS_IS_OK(status)) { if (open_was_deferred(req->mid)) { /* We have re-scheduled this call. */ - END_PROFILE(SMBctemp); - return; + goto out; } reply_openerror(req, status); - END_PROFILE(SMBctemp); - return; + goto out; } reply_outbuf(req, 1, 0); @@ -2175,8 +2297,7 @@ void reply_ctemp(struct smb_request *req) if (message_push_string(&req->outbuf, s, STR_ASCII|STR_TERMINATE) == -1) { reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBctemp); - return; + goto out; } if (oplock_request && lp_fake_oplocks(SNUM(conn))) { @@ -2191,8 +2312,9 @@ void reply_ctemp(struct smb_request *req) DEBUG( 2, ( "reply_ctemp: created temp file %s\n", fsp->fsp_name ) ); DEBUG( 3, ( "reply_ctemp %s fd=%d umode=0%o\n", fsp->fsp_name, - fsp->fh->fd, (unsigned int)sbuf.st_mode ) ); - + fsp->fh->fd, (unsigned int)smb_fname->st.st_ex_mode)); + out: + TALLOC_FREE(smb_fname); END_PROFILE(SMBctemp); return; } @@ -2215,7 +2337,17 @@ static NTSTATUS can_rename(connection_struct *conn, files_struct *fsp, return NT_STATUS_NO_SUCH_FILE; } - if (S_ISDIR(pst->st_mode)) { + if (S_ISDIR(pst->st_ex_mode)) { + if (fsp->posix_open) { + return NT_STATUS_OK; + } + + /* If no pathnames are open below this + directory, allow the rename. */ + + if (file_find_subpath(fsp)) { + return NT_STATUS_ACCESS_DENIED; + } return NT_STATUS_OK; } @@ -2232,10 +2364,10 @@ static NTSTATUS can_rename(connection_struct *conn, files_struct *fsp, static NTSTATUS do_unlink(connection_struct *conn, struct smb_request *req, - const char *fname, + struct smb_filename *smb_fname, uint32 dirtype) { - SMB_STRUCT_STAT sbuf; + char *fname = NULL; uint32 fattr; files_struct *fsp; uint32 dirtype_orig = dirtype; @@ -2247,11 +2379,16 @@ static NTSTATUS do_unlink(connection_struct *conn, return NT_STATUS_MEDIA_WRITE_PROTECTED; } - if (SMB_VFS_LSTAT(conn,fname,&sbuf) != 0) { + if (SMB_VFS_LSTAT(conn, smb_fname) != 0) { return map_nt_error_from_unix(errno); } - fattr = dos_mode(conn,fname,&sbuf); + status = get_full_smb_filename(smb_fname, smb_fname, &fname); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + fattr = dos_mode(conn, fname, &smb_fname->st); + TALLOC_FREE(fname); if (dirtype & FILE_ATTRIBUTE_NORMAL) { dirtype = aDIR|aARCH|aRONLY; @@ -2329,8 +2466,7 @@ static NTSTATUS do_unlink(connection_struct *conn, (conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - 0, /* create_file_flags */ + smb_fname, /* fname */ DELETE_ACCESS, /* access_mask */ FILE_SHARE_NONE, /* share_access */ FILE_OPEN, /* create_disposition*/ @@ -2341,8 +2477,7 @@ static NTSTATUS do_unlink(connection_struct *conn, NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - &sbuf); /* psbuf */ + NULL); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("SMB_VFS_CREATEFILE failed: %s\n", @@ -2367,31 +2502,24 @@ static NTSTATUS do_unlink(connection_struct *conn, NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, uint32 dirtype, const char *name_in, bool has_wild) { - const char *directory = NULL; - char *mask = NULL; - char *name = NULL; - char *p = NULL; + struct smb_filename *smb_fname = NULL; + char *fname_dir = NULL; + char *fname_mask = NULL; int count=0; NTSTATUS status = NT_STATUS_OK; - SMB_STRUCT_STAT sbuf; TALLOC_CTX *ctx = talloc_tos(); - status = unix_convert(ctx, conn, name_in, has_wild, &name, NULL, &sbuf); + status = unix_convert(ctx, conn, name_in, &smb_fname, + has_wild ? UCF_ALLOW_WCARD_LCOMP : 0); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - p = strrchr_m(name,'/'); - if (!p) { - directory = talloc_strdup(ctx, "."); - if (!directory) { - return NT_STATUS_NO_MEMORY; - } - mask = name; - } else { - *p = 0; - directory = name; - mask = p+1; + /* Split up the directory from the filename/mask. */ + status = split_fname_dir_mask(ctx, smb_fname->base_name, + &fname_dir, &fname_mask); + if (!NT_STATUS_IS_OK(status)) { + goto out; } /* @@ -2403,37 +2531,44 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, * Tine Smukavec . */ - if (!VALID_STAT(sbuf) && mangle_is_mangled(mask,conn->params)) { + if (!VALID_STAT(smb_fname->st) && + mangle_is_mangled(fname_mask, conn->params)) { char *new_mask = NULL; - mangle_lookup_name_from_8_3(ctx, - mask, - &new_mask, - conn->params ); + mangle_lookup_name_from_8_3(ctx, fname_mask, + &new_mask, conn->params); if (new_mask) { - mask = new_mask; + TALLOC_FREE(fname_mask); + fname_mask = new_mask; } } if (!has_wild) { - directory = talloc_asprintf(ctx, - "%s/%s", - directory, - mask); - if (!directory) { - return NT_STATUS_NO_MEMORY; + + /* + * Only one file needs to be unlinked. Append the mask back + * onto the directory. + */ + TALLOC_FREE(smb_fname->base_name); + smb_fname->base_name = talloc_asprintf(smb_fname, + "%s/%s", + fname_dir, + fname_mask); + if (!smb_fname->base_name) { + status = NT_STATUS_NO_MEMORY; + goto out; } if (dirtype == 0) { dirtype = FILE_ATTRIBUTE_NORMAL; } - status = check_name(conn, directory); + status = check_name(conn, smb_fname->base_name); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - status = do_unlink(conn, req, directory, dirtype); + status = do_unlink(conn, req, smb_fname, dirtype); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } count++; @@ -2443,23 +2578,29 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, const char *dname; if ((dirtype & SAMBA_ATTRIBUTES_MASK) == aDIR) { - return NT_STATUS_OBJECT_NAME_INVALID; + status = NT_STATUS_OBJECT_NAME_INVALID; + goto out; } - if (strequal(mask,"????????.???")) { - mask[0] = '*'; - mask[1] = '\0'; + if (strequal(fname_mask,"????????.???")) { + TALLOC_FREE(fname_mask); + fname_mask = talloc_strdup(ctx, "*"); + if (!fname_mask) { + status = NT_STATUS_NO_MEMORY; + goto out; + } } - status = check_name(conn, directory); + status = check_name(conn, fname_dir); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, + dir_hnd = OpenDir(talloc_tos(), conn, fname_dir, fname_mask, dirtype); if (dir_hnd == NULL) { - return map_nt_error_from_unix(errno); + status = map_nt_error_from_unix(errno); + goto out; } /* XXXX the CIFS spec says that if bit0 of the flags2 field is set then @@ -2469,11 +2610,10 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, status = NT_STATUS_NO_SUCH_FILE; - while ((dname = ReadDirName(dir_hnd, &offset))) { - SMB_STRUCT_STAT st; - char *fname = NULL; - - if (!is_visible_file(conn, directory, dname, &st, True)) { + while ((dname = ReadDirName(dir_hnd, &offset, + &smb_fname->st))) { + if (!is_visible_file(conn, fname_dir, dname, + &smb_fname->st, true)) { continue; } @@ -2482,34 +2622,36 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, continue; } - if(!mask_match(dname, mask, conn->case_sensitive)) { + if(!mask_match(dname, fname_mask, + conn->case_sensitive)) { continue; } - fname = talloc_asprintf(ctx, "%s/%s", - directory, - dname); - if (!fname) { - return NT_STATUS_NO_MEMORY; + TALLOC_FREE(smb_fname->base_name); + smb_fname->base_name = + talloc_asprintf(smb_fname, "%s/%s", + fname_dir, dname); + + if (!smb_fname->base_name) { + TALLOC_FREE(dir_hnd); + status = NT_STATUS_NO_MEMORY; + goto out; } - status = check_name(conn, fname); + status = check_name(conn, smb_fname->base_name); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(dir_hnd); - return status; + goto out; } - status = do_unlink(conn, req, fname, dirtype); + status = do_unlink(conn, req, smb_fname, dirtype); if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(fname); continue; } count++; DEBUG(3,("unlink_internals: successful unlink [%s]\n", - fname)); - - TALLOC_FREE(fname); + smb_fname->base_name)); } TALLOC_FREE(dir_hnd); } @@ -2518,6 +2660,10 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req, status = map_nt_error_from_unix(errno); } + out: + TALLOC_FREE(smb_fname); + TALLOC_FREE(fname_dir); + TALLOC_FREE(fname_mask); return status; } @@ -2644,7 +2790,7 @@ static ssize_t fake_sendfile(files_struct *fsp, SMB_OFF_T startpos, /* If we had a short read, fill with zeros. */ if (ret < cur_read) { - memset(buf, '\0', cur_read - ret); + memset(buf + ret, '\0', cur_read - ret); } if (write_data(smbd_server_fd(),buf,cur_read) != cur_read) { @@ -2659,6 +2805,66 @@ static ssize_t fake_sendfile(files_struct *fsp, SMB_OFF_T startpos, return (ssize_t)nread; } +#if defined(WITH_SENDFILE) +/**************************************************************************** + Deal with the case of sendfile reading less bytes from the file than + requested. Fill with zeros (all we can do). +****************************************************************************/ + +static void sendfile_short_send(files_struct *fsp, + ssize_t nread, + size_t headersize, + size_t smb_maxcnt) +{ +#define SHORT_SEND_BUFSIZE 1024 + if (nread < headersize) { + DEBUG(0,("sendfile_short_send: sendfile failed to send " + "header for file %s (%s). Terminating\n", + fsp->fsp_name, strerror(errno) )); + exit_server_cleanly("sendfile_short_send failed"); + } + + nread -= headersize; + + if (nread < smb_maxcnt) { + char *buf = SMB_CALLOC_ARRAY(char, SHORT_SEND_BUFSIZE); + if (!buf) { + exit_server_cleanly("sendfile_short_send: " + "malloc failed"); + } + + DEBUG(0,("sendfile_short_send: filling truncated file %s " + "with zeros !\n", fsp->fsp_name)); + + while (nread < smb_maxcnt) { + /* + * We asked for the real file size and told sendfile + * to not go beyond the end of the file. But it can + * happen that in between our fstat call and the + * sendfile call the file was truncated. This is very + * bad because we have already announced the larger + * number of bytes to the client. + * + * The best we can do now is to send 0-bytes, just as + * a read from a hole in a sparse file would do. + * + * This should happen rarely enough that I don't care + * about efficiency here :-) + */ + size_t to_write; + + to_write = MIN(SHORT_SEND_BUFSIZE, smb_maxcnt - nread); + if (write_data(smbd_server_fd(), buf, to_write) != to_write) { + exit_server_cleanly("sendfile_short_send: " + "write_data failed"); + } + nread += to_write; + } + SAFE_FREE(buf); + } +} +#endif /* defined WITH_SENDFILE */ + /**************************************************************************** Return a readbraw error (4 bytes of zero). ****************************************************************************/ @@ -2676,11 +2882,12 @@ static void reply_readbraw_error(void) Use sendfile in readbraw. ****************************************************************************/ -void send_file_readbraw(connection_struct *conn, - files_struct *fsp, - SMB_OFF_T startpos, - size_t nread, - ssize_t mincount) +static void send_file_readbraw(connection_struct *conn, + struct smb_request *req, + files_struct *fsp, + SMB_OFF_T startpos, + size_t nread, + ssize_t mincount) { char *outbuf = NULL; ssize_t ret=0; @@ -2693,16 +2900,18 @@ void send_file_readbraw(connection_struct *conn, * reply_readbraw has already checked the length. */ - if ( (chain_size == 0) && (nread > 0) && (fsp->base_fsp == NULL) && - (fsp->wcp == NULL) && lp_use_sendfile(SNUM(conn)) ) { + if ( !req_is_in_chain(req) && (nread > 0) && (fsp->base_fsp == NULL) && + (fsp->wcp == NULL) && + lp_use_sendfile(SNUM(conn), smbd_server_conn->smb1.signing_state) ) { + ssize_t sendfile_read = -1; char header[4]; DATA_BLOB header_blob; _smb_setlen(header,nread); header_blob = data_blob_const(header, 4); - if (SMB_VFS_SENDFILE(smbd_server_fd(), fsp, - &header_blob, startpos, nread) == -1) { + if ((sendfile_read = SMB_VFS_SENDFILE(smbd_server_fd(), fsp, + &header_blob, startpos, nread)) == -1) { /* Returning ENOSYS means no data at all was sent. * Do this as a normal read. */ if (errno == ENOSYS) { @@ -2730,13 +2939,29 @@ void send_file_readbraw(connection_struct *conn, DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n", fsp->fsp_name, strerror(errno) )); exit_server_cleanly("send_file_readbraw sendfile failed"); + } else if (sendfile_read == 0) { + /* + * Some sendfile implementations return 0 to indicate + * that there was a short read, but nothing was + * actually written to the socket. In this case, + * fallback to the normal read path so the header gets + * the correct byte count. + */ + DEBUG(3, ("send_file_readbraw: sendfile sent zero " + "bytes falling back to the normal read: " + "%s\n", fsp->fsp_name)); + goto normal_readbraw; } + /* Deal with possible short send. */ + if (sendfile_read != 4+nread) { + sendfile_short_send(fsp, sendfile_read, 4, nread); + } return; } -#endif normal_readbraw: +#endif outbuf = TALLOC_ARRAY(NULL, char, nread+4); if (!outbuf) { @@ -2775,12 +3000,14 @@ void reply_readbraw(struct smb_request *req) size_t nread = 0; SMB_OFF_T startpos; files_struct *fsp; + struct lock_struct lock; SMB_STRUCT_STAT st; SMB_OFF_T size = 0; START_PROFILE(SMBreadbraw); - if (srv_is_signing_active() || is_encrypted_packet(req->inbuf)) { + if (srv_is_signing_active(smbd_server_conn) || + is_encrypted_packet(req->inbuf)) { exit_server_cleanly("reply_readbraw: SMB signing/sealing is active - " "raw reads/writes are disallowed."); } @@ -2875,17 +3102,18 @@ void reply_readbraw(struct smb_request *req) /* ensure we don't overrun the packet size */ maxcount = MIN(65535,maxcount); - if (is_locked(fsp,(uint32)req->smbpid, - (uint64_t)maxcount, - (uint64_t)startpos, - READ_LOCK)) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)maxcount, READ_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { reply_readbraw_error(); END_PROFILE(SMBreadbraw); return; } if (SMB_VFS_FSTAT(fsp, &st) == 0) { - size = st.st_size; + size = st.st_ex_size; } if (startpos >= size) { @@ -2906,10 +3134,14 @@ void reply_readbraw(struct smb_request *req) (unsigned long)mincount, (unsigned long)nread ) ); - send_file_readbraw(conn, fsp, startpos, nread, mincount); + send_file_readbraw(conn, req, fsp, startpos, nread, mincount); DEBUG(5,("reply_readbraw finished\n")); + + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + END_PROFILE(SMBreadbraw); + return; } #undef DBGC_CLASS @@ -2930,6 +3162,7 @@ void reply_lockread(struct smb_request *req) files_struct *fsp; struct byte_range_lock *br_lck = NULL; char *p = NULL; + struct smbd_server_connection *sconn = smbd_server_conn; START_PROFILE(SMBlockread); @@ -2952,8 +3185,6 @@ void reply_lockread(struct smb_request *req) return; } - release_level_2_oplocks_on_change(fsp); - numtoread = SVAL(req->vwv+1, 0); startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0); @@ -2980,6 +3211,7 @@ void reply_lockread(struct smb_request *req) WINDOWS_LOCK, False, /* Non-blocking lock. */ &status, + NULL, NULL); TALLOC_FREE(br_lck); @@ -2993,11 +3225,12 @@ void reply_lockread(struct smb_request *req) * However the requested READ size IS affected by max_recv. Insanity.... JRA. */ - if (numtoread > max_recv) { + if (numtoread > sconn->smb1.negprot.max_recv) { DEBUG(0,("reply_lockread: requested read size (%u) is greater than maximum allowed (%u). \ Returning short read of maximum allowed for compatibility with Windows 2000.\n", - (unsigned int)numtoread, (unsigned int)max_recv )); - numtoread = MIN(numtoread,max_recv); + (unsigned int)numtoread, + (unsigned int)sconn->smb1.negprot.max_recv)); + numtoread = MIN(numtoread, sconn->smb1.negprot.max_recv); } nread = read_file(fsp,data,startpos,numtoread); @@ -3038,6 +3271,8 @@ void reply_read(struct smb_request *req) SMB_OFF_T startpos; int outsize = 0; files_struct *fsp; + struct lock_struct lock; + struct smbd_server_connection *sconn = smbd_server_conn; START_PROFILE(SMBread); @@ -3068,19 +3303,23 @@ void reply_read(struct smb_request *req) /* * The requested read size cannot be greater than max_recv. JRA. */ - if (numtoread > max_recv) { + if (numtoread > sconn->smb1.negprot.max_recv) { DEBUG(0,("reply_read: requested read size (%u) is greater than maximum allowed (%u). \ Returning short read of maximum allowed for compatibility with Windows 2000.\n", - (unsigned int)numtoread, (unsigned int)max_recv )); - numtoread = MIN(numtoread,max_recv); + (unsigned int)numtoread, + (unsigned int)sconn->smb1.negprot.max_recv)); + numtoread = MIN(numtoread, sconn->smb1.negprot.max_recv); } reply_outbuf(req, 5, numtoread+3); data = smb_buf(req->outbuf) + 3; - if (is_locked(fsp, (uint32)req->smbpid, (uint64_t)numtoread, - (uint64_t)startpos, READ_LOCK)) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)numtoread, READ_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { reply_doserror(req, ERRDOS,ERRlock); END_PROFILE(SMBread); return; @@ -3091,8 +3330,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", if (nread < 0) { reply_unixerror(req, ERRDOS,ERRnoaccess); - END_PROFILE(SMBread); - return; + goto strict_unlock; } srv_set_message((char *)req->outbuf, 5, nread+3, False); @@ -3105,6 +3343,9 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", DEBUG( 3, ( "read fnum=%d num=%d nread=%d\n", fsp->fnum, (int)numtoread, (int)nread ) ); +strict_unlock: + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + END_PROFILE(SMBread); return; } @@ -3113,7 +3354,8 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n", Setup readX header. ****************************************************************************/ -static int setup_readX_header(char *outbuf, size_t smb_maxcnt) +static int setup_readX_header(struct smb_request *req, char *outbuf, + size_t smb_maxcnt) { int outsize; char *data; @@ -3126,9 +3368,13 @@ static int setup_readX_header(char *outbuf, size_t smb_maxcnt) SCVAL(outbuf,smb_vwv0,0xFF); SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */ SSVAL(outbuf,smb_vwv5,smb_maxcnt); - SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(outbuf,smb_vwv6, + req_wct_ofs(req) + + 1 /* the wct field */ + + 12 * sizeof(uint16_t) /* vwv */ + + 2); /* the buflen field */ SSVAL(outbuf,smb_vwv7,(smb_maxcnt >> 16)); - SSVAL(smb_buf(outbuf),-2,smb_maxcnt); + SSVAL(outbuf,smb_vwv11,smb_maxcnt); /* Reset the outgoing length, set_message truncates at 0x1FFFF. */ _smb_setlen_large(outbuf,(smb_size + 12*2 + smb_maxcnt - 4)); return outsize; @@ -3144,20 +3390,29 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, { SMB_STRUCT_STAT sbuf; ssize_t nread = -1; + struct lock_struct lock; if(SMB_VFS_FSTAT(fsp, &sbuf) == -1) { reply_unixerror(req, ERRDOS, ERRnoaccess); return; } - if (startpos > sbuf.st_size) { - smb_maxcnt = 0; - } else if (smb_maxcnt > (sbuf.st_size - startpos)) { - smb_maxcnt = (sbuf.st_size - startpos); + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)smb_maxcnt, READ_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { + reply_doserror(req, ERRDOS, ERRlock); + return; } - if (smb_maxcnt == 0) { - goto normal_read; + if (!S_ISREG(sbuf.st_ex_mode) || (startpos > sbuf.st_ex_size) + || (smb_maxcnt > (sbuf.st_ex_size - startpos))) { + /* + * We already know that we would do a short read, so don't + * try the sendfile() path. + */ + goto nosendfile_read; } #if defined(WITH_SENDFILE) @@ -3167,9 +3422,10 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, * on a train in Germany :-). JRA. */ - if ((chain_size == 0) && (CVAL(req->vwv+0, 0) == 0xFF) && + if (!req_is_in_chain(req) && !is_encrypted_packet(req->inbuf) && (fsp->base_fsp == NULL) && - lp_use_sendfile(SNUM(conn)) && (fsp->wcp == NULL) ) { + (fsp->wcp == NULL) && + lp_use_sendfile(SNUM(conn), smbd_server_conn->smb1.signing_state) ) { uint8 headerbuf[smb_size + 12 * 2]; DATA_BLOB header; @@ -3182,12 +3438,12 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, header = data_blob_const(headerbuf, sizeof(headerbuf)); construct_reply_common_req(req, (char *)headerbuf); - setup_readX_header((char *)headerbuf, smb_maxcnt); + setup_readX_header(req, (char *)headerbuf, smb_maxcnt); if ((nread = SMB_VFS_SENDFILE(smbd_server_fd(), fsp, &header, startpos, smb_maxcnt)) == -1) { - /* Returning ENOSYS or EINVAL means no data at all was sent. + /* Returning ENOSYS means no data at all was sent. Do this as a normal read. */ - if (errno == ENOSYS || errno == EINVAL) { + if (errno == ENOSYS) { goto normal_read; } @@ -3211,30 +3467,48 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, DEBUG( 3, ( "send_file_readX: fake_sendfile fnum=%d max=%d nread=%d\n", fsp->fnum, (int)smb_maxcnt, (int)nread ) ); /* No outbuf here means successful sendfile. */ - TALLOC_FREE(req->outbuf); - return; + goto strict_unlock; } DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n", fsp->fsp_name, strerror(errno) )); exit_server_cleanly("send_file_readX sendfile failed"); + } else if (nread == 0) { + /* + * Some sendfile implementations return 0 to indicate + * that there was a short read, but nothing was + * actually written to the socket. In this case, + * fallback to the normal read path so the header gets + * the correct byte count. + */ + DEBUG(3, ("send_file_readX: sendfile sent zero bytes " + "falling back to the normal read: %s\n", + fsp->fsp_name)); + goto normal_read; } DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n", fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + + /* Deal with possible short send. */ + if (nread != smb_maxcnt + sizeof(headerbuf)) { + sendfile_short_send(fsp, nread, sizeof(headerbuf), smb_maxcnt); + } /* No outbuf here means successful sendfile. */ - TALLOC_FREE(req->outbuf); - return; + SMB_PERFCOUNT_SET_MSGLEN_OUT(&req->pcd, nread); + SMB_PERFCOUNT_END(&req->pcd); + goto strict_unlock; } -#endif normal_read: +#endif + if ((smb_maxcnt & 0xFF0000) > 0x10000) { uint8 headerbuf[smb_size + 2*12]; construct_reply_common_req(req, (char *)headerbuf); - setup_readX_header((char *)headerbuf, smb_maxcnt); + setup_readX_header(req, (char *)headerbuf, smb_maxcnt); /* Send out the header. */ if (write_data(smbd_server_fd(), (char *)headerbuf, @@ -3249,24 +3523,34 @@ normal_read: fsp->fsp_name, strerror(errno) )); exit_server_cleanly("send_file_readX: fake_sendfile failed"); } - TALLOC_FREE(req->outbuf); - return; + goto strict_unlock; } +nosendfile_read: + reply_outbuf(req, 12, smb_maxcnt); nread = read_file(fsp, smb_buf(req->outbuf), startpos, smb_maxcnt); + + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + if (nread < 0) { reply_unixerror(req, ERRDOS, ERRnoaccess); return; } - setup_readX_header((char *)req->outbuf, nread); + setup_readX_header(req, (char *)req->outbuf, nread); DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n", fsp->fnum, (int)smb_maxcnt, (int)nread ) ); chain_reply(req); + return; + + strict_unlock: + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + TALLOC_FREE(req->outbuf); + return; } /**************************************************************************** @@ -3324,7 +3608,8 @@ void reply_read_and_X(struct smb_request *req) return; } /* We currently don't do this on signed or sealed data. */ - if (srv_is_signing_active() || is_encrypted_packet(req->inbuf)) { + if (srv_is_signing_active(smbd_server_conn) || + is_encrypted_packet(req->inbuf)) { reply_nterror(req, NT_STATUS_NOT_SUPPORTED); END_PROFILE(SMBreadX); return; @@ -3366,21 +3651,14 @@ void reply_read_and_X(struct smb_request *req) } - if (is_locked(fsp, (uint32)req->smbpid, (uint64_t)smb_maxcnt, - (uint64_t)startpos, READ_LOCK)) { - END_PROFILE(SMBreadX); - reply_doserror(req, ERRDOS, ERRlock); - return; - } - if (!big_readX && schedule_aio_read_and_X(conn, req, fsp, startpos, smb_maxcnt)) { - END_PROFILE(SMBreadX); - return; + goto out; } send_file_readX(conn, req, fsp, startpos, smb_maxcnt); + out: END_PROFILE(SMBreadX); return; } @@ -3415,6 +3693,7 @@ void reply_writebraw(struct smb_request *req) char *data=NULL; bool write_through; files_struct *fsp; + struct lock_struct lock; NTSTATUS status; START_PROFILE(SMBwritebraw); @@ -3426,7 +3705,7 @@ void reply_writebraw(struct smb_request *req) */ SCVAL(req->inbuf,smb_com,SMBwritec); - if (srv_is_signing_active()) { + if (srv_is_signing_active(smbd_server_conn)) { END_PROFILE(SMBwritebraw); exit_server_cleanly("reply_writebraw: SMB signing is active - " "raw reads/writes are disallowed."); @@ -3476,8 +3755,11 @@ void reply_writebraw(struct smb_request *req) return; } - if (is_locked(fsp,(uint32)req->smbpid,(uint64_t)tcount, - (uint64_t)startpos, WRITE_LOCK)) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)tcount, WRITE_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { reply_doserror(req, ERRDOS, ERRlock); error_to_writebrawerr(req); END_PROFILE(SMBwritebraw); @@ -3496,8 +3778,7 @@ void reply_writebraw(struct smb_request *req) if (nwritten < (ssize_t)numtowrite) { reply_unixerror(req, ERRHRD, ERRdiskfull); error_to_writebrawerr(req); - END_PROFILE(SMBwritebraw); - return; + goto strict_unlock; } total_written = nwritten; @@ -3507,8 +3788,7 @@ void reply_writebraw(struct smb_request *req) if (!buf) { reply_doserror(req, ERRDOS, ERRnomem); error_to_writebrawerr(req); - END_PROFILE(SMBwritebraw); - return; + goto strict_unlock; } /* Return a SMBwritebraw message to the redirector to tell @@ -3520,8 +3800,10 @@ void reply_writebraw(struct smb_request *req) SSVALS(buf,smb_vwv0,0xFFFF); show_msg(buf); if (!srv_send_smb(smbd_server_fd(), - buf, - IS_CONN_ENCRYPTED(conn))) { + buf, + false, 0, /* no signing */ + IS_CONN_ENCRYPTED(conn), + &req->pcd)) { exit_server_cleanly("reply_writebraw: srv_send_smb " "failed."); } @@ -3565,8 +3847,7 @@ void reply_writebraw(struct smb_request *req) TALLOC_FREE(buf); reply_unixerror(req, ERRHRD, ERRdiskfull); error_to_writebrawerr(req); - END_PROFILE(SMBwritebraw); - return; + goto strict_unlock; } if (nwritten < (ssize_t)numtowrite) { @@ -3588,8 +3869,7 @@ void reply_writebraw(struct smb_request *req) fsp->fsp_name, nt_errstr(status) )); reply_nterror(req, status); error_to_writebrawerr(req); - END_PROFILE(SMBwritebraw); - return; + goto strict_unlock; } DEBUG(3,("reply_writebraw: secondart write fnum=%d start=%.0f num=%d " @@ -3597,6 +3877,8 @@ void reply_writebraw(struct smb_request *req) fsp->fnum, (double)startpos, (int)numtowrite, (int)total_written)); + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + /* We won't return a status if write through is not selected - this * follows what WfWg does */ END_PROFILE(SMBwritebraw); @@ -3617,6 +3899,12 @@ void reply_writebraw(struct smb_request *req) TALLOC_FREE(req->outbuf); } return; + +strict_unlock: + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + + END_PROFILE(SMBwritebraw); + return; } #undef DBGC_CLASS @@ -3635,6 +3923,7 @@ void reply_writeunlock(struct smb_request *req) const char *data; NTSTATUS status = NT_STATUS_OK; files_struct *fsp; + struct lock_struct lock; START_PROFILE(SMBwriteunlock); @@ -3661,12 +3950,16 @@ void reply_writeunlock(struct smb_request *req) startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0); data = (const char *)req->buf + 3; - if (numtowrite - && is_locked(fsp, (uint32)req->smbpid, (uint64_t)numtowrite, - (uint64_t)startpos, WRITE_LOCK)) { - reply_doserror(req, ERRDOS, ERRlock); - END_PROFILE(SMBwriteunlock); - return; + if (numtowrite) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)numtowrite, WRITE_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { + reply_doserror(req, ERRDOS, ERRlock); + END_PROFILE(SMBwriteunlock); + return; + } } /* The special X/Open SMB protocol handling of @@ -3683,14 +3976,12 @@ void reply_writeunlock(struct smb_request *req) DEBUG(5,("reply_writeunlock: sync_file for %s returned %s\n", fsp->fsp_name, nt_errstr(status) )); reply_nterror(req, status); - END_PROFILE(SMBwriteunlock); - return; + goto strict_unlock; } if(((nwritten < numtowrite) && (numtowrite != 0))||(nwritten < 0)) { reply_unixerror(req, ERRHRD, ERRdiskfull); - END_PROFILE(SMBwriteunlock); - return; + goto strict_unlock; } if (numtowrite) { @@ -3703,8 +3994,7 @@ void reply_writeunlock(struct smb_request *req) if (NT_STATUS_V(status)) { reply_nterror(req, status); - END_PROFILE(SMBwriteunlock); - return; + goto strict_unlock; } } @@ -3715,6 +4005,11 @@ void reply_writeunlock(struct smb_request *req) DEBUG(3,("writeunlock fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten)); +strict_unlock: + if (numtowrite) { + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + } + END_PROFILE(SMBwriteunlock); return; } @@ -3734,6 +4029,7 @@ void reply_write(struct smb_request *req) SMB_OFF_T startpos; const char *data; files_struct *fsp; + struct lock_struct lock; NTSTATUS status; START_PROFILE(SMBwrite); @@ -3768,8 +4064,11 @@ void reply_write(struct smb_request *req) startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0); data = (const char *)req->buf + 3; - if (is_locked(fsp, (uint32)req->smbpid, (uint64_t)numtowrite, - (uint64_t)startpos, WRITE_LOCK)) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)numtowrite, WRITE_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { reply_doserror(req, ERRDOS, ERRlock); END_PROFILE(SMBwrite); return; @@ -3788,14 +4087,12 @@ void reply_write(struct smb_request *req) nwritten = vfs_allocate_file_space(fsp, (SMB_OFF_T)startpos); if (nwritten < 0) { reply_nterror(req, NT_STATUS_DISK_FULL); - END_PROFILE(SMBwrite); - return; + goto strict_unlock; } nwritten = vfs_set_filelen(fsp, (SMB_OFF_T)startpos); if (nwritten < 0) { reply_nterror(req, NT_STATUS_DISK_FULL); - END_PROFILE(SMBwrite); - return; + goto strict_unlock; } trigger_write_time_update_immediate(fsp); } else { @@ -3807,14 +4104,12 @@ void reply_write(struct smb_request *req) DEBUG(5,("reply_write: sync_file for %s returned %s\n", fsp->fsp_name, nt_errstr(status) )); reply_nterror(req, status); - END_PROFILE(SMBwrite); - return; + goto strict_unlock; } if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { reply_unixerror(req, ERRHRD, ERRdiskfull); - END_PROFILE(SMBwrite); - return; + goto strict_unlock; } reply_outbuf(req, 1, 0); @@ -3828,6 +4123,9 @@ void reply_write(struct smb_request *req) DEBUG(3,("write fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten)); +strict_unlock: + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + END_PROFILE(SMBwrite); return; } @@ -3846,6 +4144,7 @@ bool is_valid_writeX_buffer(const uint8_t *inbuf) connection_struct *conn = NULL; unsigned int doff = 0; size_t len = smb_len_large(inbuf); + struct smbd_server_connection *sconn = smbd_server_conn; if (is_encrypted_packet(inbuf)) { /* Can't do this on encrypted @@ -3864,7 +4163,7 @@ bool is_valid_writeX_buffer(const uint8_t *inbuf) return false; } - conn = conn_find(SVAL(inbuf, smb_tid)); + conn = conn_find(sconn, SVAL(inbuf, smb_tid)); if (conn == NULL) { DEBUG(10,("is_valid_writeX_buffer: bad tid\n")); return false; @@ -3873,6 +4172,10 @@ bool is_valid_writeX_buffer(const uint8_t *inbuf) DEBUG(10,("is_valid_writeX_buffer: IPC$ tid\n")); return false; } + if (IS_PRINT(conn)) { + DEBUG(10,("is_valid_writeX_buffer: printing tid\n")); + return false; + } doff = SVAL(inbuf,smb_vwv11); numtowrite = SVAL(inbuf,smb_vwv10); @@ -3921,6 +4224,7 @@ void reply_write_and_X(struct smb_request *req) { connection_struct *conn = req->conn; files_struct *fsp; + struct lock_struct lock; SMB_OFF_T startpos; size_t numtowrite; bool write_through; @@ -4023,9 +4327,11 @@ void reply_write_and_X(struct smb_request *req) #endif /* LARGE_SMB_OFF_T */ } - if (is_locked(fsp,(uint32)req->smbpid, - (uint64_t)numtowrite, - (uint64_t)startpos, WRITE_LOCK)) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)numtowrite, WRITE_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { reply_doserror(req, ERRDOS, ERRlock); END_PROFILE(SMBwriteX); return; @@ -4043,8 +4349,7 @@ void reply_write_and_X(struct smb_request *req) if ((req->unread_bytes == 0) && schedule_aio_write_and_X(conn, req, fsp, data, startpos, numtowrite)) { - END_PROFILE(SMBwriteX); - return; + goto strict_unlock; } nwritten = write_file(req,fsp,data,startpos,numtowrite); @@ -4052,8 +4357,7 @@ void reply_write_and_X(struct smb_request *req) if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { reply_unixerror(req, ERRHRD, ERRdiskfull); - END_PROFILE(SMBwriteX); - return; + goto strict_unlock; } reply_outbuf(req, 6, 0); @@ -4073,13 +4377,20 @@ void reply_write_and_X(struct smb_request *req) DEBUG(5,("reply_write_and_X: sync_file for %s returned %s\n", fsp->fsp_name, nt_errstr(status) )); reply_nterror(req, status); - END_PROFILE(SMBwriteX); - return; + goto strict_unlock; } + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + END_PROFILE(SMBwriteX); chain_reply(req); return; + +strict_unlock: + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + + END_PROFILE(SMBwriteX); + return; } /**************************************************************************** @@ -4145,7 +4456,7 @@ void reply_lseek(struct smb_request *req) return; } - current_pos += sbuf.st_size; + current_pos += sbuf.st_ex_size; if(current_pos < 0) res = SMB_VFS_LSEEK(fsp,0,SEEK_SET); } @@ -4319,6 +4630,7 @@ void reply_writeclose(struct smb_request *req) const char *data; struct timespec mtime; files_struct *fsp; + struct lock_struct lock; START_PROFILE(SMBwriteclose); @@ -4345,12 +4657,16 @@ void reply_writeclose(struct smb_request *req) mtime = convert_time_t_to_timespec(srv_make_unix_date3(req->vwv+4)); data = (const char *)req->buf + 1; - if (numtowrite - && is_locked(fsp, (uint32)req->smbpid, (uint64_t)numtowrite, - (uint64_t)startpos, WRITE_LOCK)) { - reply_doserror(req, ERRDOS,ERRlock); - END_PROFILE(SMBwriteclose); - return; + if (numtowrite) { + init_strict_lock_struct(fsp, (uint32)req->smbpid, + (uint64_t)startpos, (uint64_t)numtowrite, WRITE_LOCK, + &lock); + + if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { + reply_doserror(req, ERRDOS,ERRlock); + END_PROFILE(SMBwriteclose); + return; + } } nwritten = write_file(req,fsp,data,startpos,numtowrite); @@ -4374,19 +4690,23 @@ void reply_writeclose(struct smb_request *req) if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) { reply_doserror(req, ERRHRD, ERRdiskfull); - END_PROFILE(SMBwriteclose); - return; + goto strict_unlock; } if(!NT_STATUS_IS_OK(close_status)) { reply_nterror(req, close_status); - END_PROFILE(SMBwriteclose); - return; + goto strict_unlock; } reply_outbuf(req, 1, 0); SSVAL(req->outbuf,smb_vwv0,nwritten); + +strict_unlock: + if (numtowrite) { + SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); + } + END_PROFILE(SMBwriteclose); return; } @@ -4421,8 +4741,6 @@ void reply_lock(struct smb_request *req) return; } - release_level_2_oplocks_on_change(fsp); - count = (uint64_t)IVAL(req->vwv+1, 0); offset = (uint64_t)IVAL(req->vwv+3, 0); @@ -4438,6 +4756,7 @@ void reply_lock(struct smb_request *req) WINDOWS_LOCK, False, /* Non-blocking lock. */ &status, + NULL, NULL); TALLOC_FREE(br_lck); @@ -4515,6 +4834,7 @@ void reply_unlock(struct smb_request *req) void reply_tdis(struct smb_request *req) { + struct smbd_server_connection *sconn = smbd_server_conn; connection_struct *conn = req->conn; START_PROFILE(SMBtdis); @@ -4527,7 +4847,7 @@ void reply_tdis(struct smb_request *req) conn->used = False; - close_cnum(conn,req->vuid); + close_cnum(sconn, conn,req->vuid); req->conn = NULL; reply_outbuf(req, 0, 0); @@ -4543,11 +4863,15 @@ void reply_tdis(struct smb_request *req) void reply_echo(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_perfcount_data local_pcd; + struct smb_perfcount_data *cur_pcd; int smb_reverb; int seq_num; START_PROFILE(SMBecho); + smb_init_perfcount_data(&local_pcd); + if (req->wct < 1) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); END_PROFILE(SMBecho); @@ -4568,13 +4892,24 @@ void reply_echo(struct smb_request *req) smb_reverb = 100; } - for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) { + for (seq_num = 1 ; seq_num <= smb_reverb ; seq_num++) { + + /* this makes sure we catch the request pcd */ + if (seq_num == smb_reverb) { + cur_pcd = &req->pcd; + } else { + SMB_PERFCOUNT_COPY_CONTEXT(&req->pcd, &local_pcd); + cur_pcd = &local_pcd; + } + SSVAL(req->outbuf,smb_vwv0,seq_num); show_msg((char *)req->outbuf); if (!srv_send_smb(smbd_server_fd(), (char *)req->outbuf, - IS_CONN_ENCRYPTED(conn)||req->encrypted)) + true, req->seqnum+1, + IS_CONN_ENCRYPTED(conn)||req->encrypted, + cur_pcd)) exit_server_cleanly("reply_echo: srv_send_smb failed."); } @@ -4594,6 +4929,7 @@ void reply_printopen(struct smb_request *req) { connection_struct *conn = req->conn; files_struct *fsp; + SMB_STRUCT_STAT sbuf; NTSTATUS status; START_PROFILE(SMBsplopen); @@ -4618,9 +4954,10 @@ void reply_printopen(struct smb_request *req) } /* Open for exclusive use, write only. */ - status = print_fsp_open(req, conn, NULL, req->vuid, fsp); + status = print_fsp_open(req, conn, NULL, req->vuid, fsp, &sbuf); if (!NT_STATUS_IS_OK(status)) { + file_free(req, fsp); reply_nterror(req, status); END_PROFILE(SMBsplopen); return; @@ -4844,9 +5181,9 @@ void reply_printwrite(struct smb_request *req) void reply_mkdir(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_dname = NULL; char *directory = NULL; NTSTATUS status; - SMB_STRUCT_STAT sbuf; TALLOC_CTX *ctx = talloc_tos(); START_PROFILE(SMBmkdir); @@ -4855,41 +5192,25 @@ void reply_mkdir(struct smb_request *req) STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBmkdir); - return; + goto out; } - status = resolve_dfspath(ctx, conn, + status = filename_convert(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, directory, - &directory); + &smb_dname, + NULL); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBmkdir); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBmkdir); - return; - } - - status = unix_convert(ctx, conn, directory, False, &directory, NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBmkdir); - return; - } - - status = check_name(conn, directory); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBmkdir); - return; + goto out; } - status = create_directory(conn, req, directory); + status = create_directory(conn, req, smb_dname); DEBUG(5, ("create_directory returned %s\n", nt_errstr(status))); @@ -4907,14 +5228,14 @@ void reply_mkdir(struct smb_request *req) } reply_nterror(req, status); - END_PROFILE(SMBmkdir); - return; + goto out; } reply_outbuf(req, 0, 0); - DEBUG( 3, ( "mkdir %s\n", directory ) ); - + DEBUG(3, ("mkdir %s\n", smb_dname->base_name)); + out: + TALLOC_FREE(smb_dname); END_PROFILE(SMBmkdir); return; } @@ -4926,59 +5247,78 @@ void reply_mkdir(struct smb_request *req) static bool recursive_rmdir(TALLOC_CTX *ctx, connection_struct *conn, - char *directory) + struct smb_filename *smb_dname) { const char *dname = NULL; bool ret = True; long offset = 0; - struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, directory, - NULL, 0); + SMB_STRUCT_STAT st; + struct smb_Dir *dir_hnd; + SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); + + dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0); if(dir_hnd == NULL) return False; - while((dname = ReadDirName(dir_hnd, &offset))) { + while((dname = ReadDirName(dir_hnd, &offset, &st))) { + struct smb_filename *smb_dname_full = NULL; char *fullname = NULL; - SMB_STRUCT_STAT st; + bool do_break = true; + NTSTATUS status; if (ISDOT(dname) || ISDOTDOT(dname)) { continue; } - if (!is_visible_file(conn, directory, dname, &st, False)) { + if (!is_visible_file(conn, smb_dname->base_name, dname, &st, + false)) { continue; } /* Construct the full name. */ fullname = talloc_asprintf(ctx, "%s/%s", - directory, + smb_dname->base_name, dname); if (!fullname) { errno = ENOMEM; - ret = False; - break; + goto err_break; } - if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) { - ret = False; - break; + status = create_synthetic_smb_fname(talloc_tos(), fullname, + NULL, NULL, + &smb_dname_full); + if (!NT_STATUS_IS_OK(status)) { + goto err_break; } - if(st.st_mode & S_IFDIR) { - if(!recursive_rmdir(ctx, conn, fullname)) { - ret = False; - break; + if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { + goto err_break; + } + + if(smb_dname_full->st.st_ex_mode & S_IFDIR) { + if(!recursive_rmdir(ctx, conn, smb_dname_full)) { + goto err_break; } - if(SMB_VFS_RMDIR(conn,fullname) != 0) { - ret = False; - break; + if(SMB_VFS_RMDIR(conn, + smb_dname_full->base_name) != 0) { + goto err_break; } - } else if(SMB_VFS_UNLINK(conn,fullname) != 0) { - ret = False; - break; + } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { + goto err_break; } + + /* Successful iteration. */ + do_break = false; + + err_break: + TALLOC_FREE(smb_dname_full); TALLOC_FREE(fullname); + if (do_break) { + ret = false; + break; + } } TALLOC_FREE(dir_hnd); return ret; @@ -4989,33 +5329,35 @@ static bool recursive_rmdir(TALLOC_CTX *ctx, ****************************************************************************/ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, - connection_struct *conn, - const char *directory) + connection_struct *conn, + struct smb_filename *smb_dname) { int ret; SMB_STRUCT_STAT st; + SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname)); + /* Might be a symlink. */ - if(SMB_VFS_LSTAT(conn, directory, &st) != 0) { + if(SMB_VFS_LSTAT(conn, smb_dname) != 0) { return map_nt_error_from_unix(errno); } - if (S_ISLNK(st.st_mode)) { + if (S_ISLNK(smb_dname->st.st_ex_mode)) { /* Is what it points to a directory ? */ - if(SMB_VFS_STAT(conn, directory, &st) != 0) { + if(SMB_VFS_STAT(conn, smb_dname) != 0) { return map_nt_error_from_unix(errno); } - if (!(S_ISDIR(st.st_mode))) { + if (!(S_ISDIR(smb_dname->st.st_ex_mode))) { return NT_STATUS_NOT_A_DIRECTORY; } - ret = SMB_VFS_UNLINK(conn,directory); + ret = SMB_VFS_UNLINK(conn, smb_dname); } else { - ret = SMB_VFS_RMDIR(conn,directory); + ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); } if (ret == 0) { notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, - directory); + smb_dname->base_name); return NT_STATUS_OK; } @@ -5029,17 +5371,19 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, const char *dname; long dirpos = 0; struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn, - directory, NULL, 0); + smb_dname->base_name, NULL, + 0); if(dir_hnd == NULL) { errno = ENOTEMPTY; goto err; } - while ((dname = ReadDirName(dir_hnd,&dirpos))) { + while ((dname = ReadDirName(dir_hnd, &dirpos, &st))) { if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) continue; - if (!is_visible_file(conn, directory, dname, &st, False)) + if (!is_visible_file(conn, smb_dname->base_name, dname, + &st, false)) continue; if(!IS_VETO_PATH(conn, dname)) { TALLOC_FREE(dir_hnd); @@ -5059,57 +5403,81 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, /* Do a recursive delete. */ RewindDir(dir_hnd,&dirpos); - while ((dname = ReadDirName(dir_hnd,&dirpos))) { + while ((dname = ReadDirName(dir_hnd, &dirpos, &st))) { + struct smb_filename *smb_dname_full = NULL; char *fullname = NULL; + bool do_break = true; + NTSTATUS status; if (ISDOT(dname) || ISDOTDOT(dname)) { continue; } - if (!is_visible_file(conn, directory, dname, &st, False)) { + if (!is_visible_file(conn, smb_dname->base_name, dname, + &st, false)) { continue; } fullname = talloc_asprintf(ctx, "%s/%s", - directory, + smb_dname->base_name, dname); if(!fullname) { errno = ENOMEM; - break; + goto err_break; } - if(SMB_VFS_LSTAT(conn,fullname, &st) != 0) { - break; + status = create_synthetic_smb_fname(talloc_tos(), + fullname, NULL, + NULL, + &smb_dname_full); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + goto err_break; } - if(st.st_mode & S_IFDIR) { - if(!recursive_rmdir(ctx, conn, fullname)) { - break; + + if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) { + goto err_break; + } + if(smb_dname_full->st.st_ex_mode & S_IFDIR) { + if(!recursive_rmdir(ctx, conn, + smb_dname_full)) { + goto err_break; } - if(SMB_VFS_RMDIR(conn,fullname) != 0) { - break; + if(SMB_VFS_RMDIR(conn, + smb_dname_full->base_name) != 0) { + goto err_break; } - } else if(SMB_VFS_UNLINK(conn,fullname) != 0) { - break; + } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) { + goto err_break; } + + /* Successful iteration. */ + do_break = false; + + err_break: TALLOC_FREE(fullname); + TALLOC_FREE(smb_dname_full); + if (do_break) + break; } TALLOC_FREE(dir_hnd); /* Retry the rmdir */ - ret = SMB_VFS_RMDIR(conn,directory); + ret = SMB_VFS_RMDIR(conn, smb_dname->base_name); } err: if (ret != 0) { DEBUG(3,("rmdir_internals: couldn't remove directory %s : " - "%s\n", directory,strerror(errno))); + "%s\n", smb_fname_str_dbg(smb_dname), + strerror(errno))); return map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, - directory); + smb_dname->base_name); return NT_STATUS_OK; } @@ -5121,8 +5489,8 @@ NTSTATUS rmdir_internals(TALLOC_CTX *ctx, void reply_rmdir(struct smb_request *req) { connection_struct *conn = req->conn; + struct smb_filename *smb_dname = NULL; char *directory = NULL; - SMB_STRUCT_STAT sbuf; NTSTATUS status; TALLOC_CTX *ctx = talloc_tos(); @@ -5132,53 +5500,36 @@ void reply_rmdir(struct smb_request *req) STR_TERMINATE, &status); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBrmdir); - return; + goto out; } - status = resolve_dfspath(ctx, conn, + status = filename_convert(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, directory, + &smb_dname, &directory); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBrmdir); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBrmdir); - return; - } - - status = unix_convert(ctx, conn, directory, False, &directory, - NULL, &sbuf); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBrmdir); - return; - } - - status = check_name(conn, directory); - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - END_PROFILE(SMBrmdir); - return; + goto out; } dptr_closepath(directory, req->smbpid); - status = rmdir_internals(ctx, conn, directory); + status = rmdir_internals(ctx, conn, smb_dname); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBrmdir); - return; + goto out; } reply_outbuf(req, 0, 0); DEBUG( 3, ( "rmdir %s\n", directory ) ); - + out: + TALLOC_FREE(smb_dname); END_PROFILE(SMBrmdir); return; } @@ -5317,10 +5668,18 @@ static bool resolve_wildcards(TALLOC_CTX *ctx, static void rename_open_files(connection_struct *conn, struct share_mode_lock *lck, - const char *newname) + const struct smb_filename *smb_fname_dst) { files_struct *fsp; bool did_rename = False; + char *fname_dst = NULL; + NTSTATUS status; + + status = get_full_smb_filename(talloc_tos(), smb_fname_dst, + &fname_dst); + if (!NT_STATUS_IS_OK(status)) { + return; + } for(fsp = file_find_di_first(lck->id); fsp; fsp = file_find_di_next(fsp)) { @@ -5331,21 +5690,24 @@ static void rename_open_files(connection_struct *conn, if (!strequal(fsp->conn->connectpath, conn->connectpath)) { continue; } - DEBUG(10,("rename_open_files: renaming file fnum %d (file_id %s) from %s -> %s\n", - fsp->fnum, file_id_string_tos(&fsp->file_id), - fsp->fsp_name, newname )); - string_set(&fsp->fsp_name, newname); + DEBUG(10, ("rename_open_files: renaming file fnum %d " + "(file_id %s) from %s -> %s\n", fsp->fnum, + file_id_string_tos(&fsp->file_id), fsp->fsp_name, + smb_fname_str_dbg(smb_fname_dst))); + string_set(&fsp->fsp_name, fname_dst); did_rename = True; } if (!did_rename) { - DEBUG(10,("rename_open_files: no open files on file_id %s for %s\n", - file_id_string_tos(&lck->id), newname )); + DEBUG(10, ("rename_open_files: no open files on file_id %s " + "for %s\n", file_id_string_tos(&lck->id), + smb_fname_str_dbg(smb_fname_dst))); } /* Send messages to all smbd's (not ourself) that the name has changed. */ rename_share_filename(smbd_messaging_context(), lck, conn->connectpath, - newname); + fname_dst); + TALLOC_FREE(fname_dst); } /**************************************************************************** @@ -5359,10 +5721,11 @@ static void rename_open_files(connection_struct *conn, report from . ****************************************************************************/ -static bool rename_path_prefix_equal(const char *src, const char *dest) +static bool rename_path_prefix_equal(const struct smb_filename *smb_fname_src, + const struct smb_filename *smb_fname_dst) { - const char *psrc = src; - const char *pdst = dest; + const char *psrc = smb_fname_src->base_name; + const char *pdst = smb_fname_dst->base_name; size_t slen; if (psrc[0] == '.' && psrc[1] == '/') { @@ -5382,31 +5745,45 @@ static bool rename_path_prefix_equal(const char *src, const char *dest) */ static void notify_rename(connection_struct *conn, bool is_dir, - const char *oldpath, const char *newpath) + const struct smb_filename *smb_fname_src, + const struct smb_filename *smb_fname_dst) { - char *olddir, *newdir; - const char *oldname, *newname; + char *parent_dir_src = NULL; + char *parent_dir_dst = NULL; + char *fname_src = NULL; + char *fname_dst = NULL; + NTSTATUS status; uint32 mask; mask = is_dir ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME; - if (!parent_dirname_talloc(NULL, oldpath, &olddir, &oldname) - || !parent_dirname_talloc(NULL, newpath, &newdir, &newname)) { - TALLOC_FREE(olddir); - return; + if (!parent_dirname(talloc_tos(), smb_fname_src->base_name, + &parent_dir_src, NULL) || + !parent_dirname(talloc_tos(), smb_fname_dst->base_name, + &parent_dir_dst, NULL)) { + goto out; + } + + status = get_full_smb_filename(talloc_tos(), smb_fname_src, + &fname_src); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + status = get_full_smb_filename(talloc_tos(), smb_fname_dst, + &fname_dst); + if (!NT_STATUS_IS_OK(status)) { + goto out; } - if (strcmp(olddir, newdir) == 0) { - notify_fname(conn, NOTIFY_ACTION_OLD_NAME, mask, oldpath); - notify_fname(conn, NOTIFY_ACTION_NEW_NAME, mask, newpath); + if (strcmp(parent_dir_src, parent_dir_dst) == 0) { + notify_fname(conn, NOTIFY_ACTION_OLD_NAME, mask, fname_src); + notify_fname(conn, NOTIFY_ACTION_NEW_NAME, mask, fname_dst); } else { - notify_fname(conn, NOTIFY_ACTION_REMOVED, mask, oldpath); - notify_fname(conn, NOTIFY_ACTION_ADDED, mask, newpath); + notify_fname(conn, NOTIFY_ACTION_REMOVED, mask, fname_src); + notify_fname(conn, NOTIFY_ACTION_ADDED, mask, fname_dst); } - TALLOC_FREE(olddir); - TALLOC_FREE(newdir); /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES and CHANGE_CREATION on the new file when renaming @@ -5415,8 +5792,13 @@ static void notify_rename(connection_struct *conn, bool is_dir, notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES |FILE_NOTIFY_CHANGE_CREATION, - newpath); + fname_dst); } + out: + TALLOC_FREE(parent_dir_src); + TALLOC_FREE(parent_dir_dst); + TALLOC_FREE(fname_src); + TALLOC_FREE(fname_dst); } /**************************************************************************** @@ -5425,32 +5807,52 @@ static void notify_rename(connection_struct *conn, bool is_dir, NTSTATUS rename_internals_fsp(connection_struct *conn, files_struct *fsp, - char *newname, - const char *newname_last_component, + const struct smb_filename *smb_fname_dst_in, uint32 attrs, bool replace_if_exists) { TALLOC_CTX *ctx = talloc_tos(); - SMB_STRUCT_STAT sbuf, sbuf1; + struct smb_filename *smb_fname_src = NULL; + struct smb_filename *smb_fname_dst = NULL; + SMB_STRUCT_STAT sbuf; NTSTATUS status = NT_STATUS_OK; struct share_mode_lock *lck = NULL; - bool dst_exists; + bool dst_exists, old_is_stream, new_is_stream; ZERO_STRUCT(sbuf); - status = check_name(conn, newname); + status = check_name(conn, smb_fname_dst_in->base_name); if (!NT_STATUS_IS_OK(status)) { return status; } - /* Ensure newname contains a '/' */ - if(strrchr_m(newname,'/') == 0) { - newname = talloc_asprintf(ctx, - "./%s", - newname); - if (!newname) { - return NT_STATUS_NO_MEMORY; + /* Make a copy of the src and dst smb_fname structs */ + status = copy_smb_filename(ctx, smb_fname_dst_in, &smb_fname_dst); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* + * This will be replaced with copy_smb_filename() when fsp->fsp_name + * is converted to store an smb_filename struct. + */ + status = create_synthetic_smb_fname_split(ctx, fsp->fsp_name, NULL, + &smb_fname_src); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* Ensure the dst smb_fname contains a '/' */ + if(strrchr_m(smb_fname_dst->base_name,'/') == 0) { + char * tmp; + tmp = talloc_asprintf(smb_fname_dst, "./%s", + smb_fname_dst->base_name); + if (!tmp) { + status = NT_STATUS_NO_MEMORY; + goto out; } + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = tmp; } /* @@ -5460,36 +5862,78 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, * the rename (user is trying to change the case of the * filename). */ - if((conn->case_sensitive == False) && (conn->case_preserve == True) && - strequal(newname, fsp->fsp_name)) { - char *p; - char *newname_modified_last_component = NULL; + strequal(smb_fname_src->base_name, smb_fname_dst->base_name) && + strequal(smb_fname_src->stream_name, smb_fname_dst->stream_name)) { + char *last_slash; + char *fname_dst_lcomp_base_mod = NULL; + struct smb_filename *smb_fname_orig_lcomp = NULL; + + /* + * Get the last component of the destination name. Note that + * we guarantee that destination name contains a '/' character + * above. + */ + last_slash = strrchr_m(smb_fname_dst->base_name, '/'); + fname_dst_lcomp_base_mod = talloc_strdup(ctx, last_slash + 1); + if (!fname_dst_lcomp_base_mod) { + status = NT_STATUS_NO_MEMORY; + goto out; + } /* - * Get the last component of the modified name. - * Note that we guarantee that newname contains a '/' - * character above. + * Create an smb_filename struct using the original last + * component of the destination. */ - p = strrchr_m(newname,'/'); - newname_modified_last_component = talloc_strdup(ctx, - p+1); - if (!newname_modified_last_component) { - return NT_STATUS_NO_MEMORY; + status = create_synthetic_smb_fname_split(ctx, + smb_fname_dst->original_lcomp, NULL, + &smb_fname_orig_lcomp); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(fname_dst_lcomp_base_mod); + goto out; } - if(strcsequal(newname_modified_last_component, - newname_last_component) == False) { + /* If the base names only differ by case, use original. */ + if(!strcsequal(fname_dst_lcomp_base_mod, + smb_fname_orig_lcomp->base_name)) { + char *tmp; /* - * Replace the modified last component with - * the original. + * Replace the modified last component with the + * original. */ - *p = '\0'; /* Truncate at the '/' */ - newname = talloc_asprintf(ctx, + *last_slash = '\0'; /* Truncate at the '/' */ + tmp = talloc_asprintf(smb_fname_dst, "%s/%s", - newname, - newname_last_component); + smb_fname_dst->base_name, + smb_fname_orig_lcomp->base_name); + if (tmp == NULL) { + status = NT_STATUS_NO_MEMORY; + TALLOC_FREE(fname_dst_lcomp_base_mod); + TALLOC_FREE(smb_fname_orig_lcomp); + goto out; + } + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = tmp; + } + + /* If the stream_names only differ by case, use original. */ + if(!strcsequal(smb_fname_dst->stream_name, + smb_fname_orig_lcomp->stream_name)) { + char *tmp = NULL; + /* Use the original stream. */ + tmp = talloc_strdup(smb_fname_dst, + smb_fname_orig_lcomp->stream_name); + if (tmp == NULL) { + status = NT_STATUS_NO_MEMORY; + TALLOC_FREE(fname_dst_lcomp_base_mod); + TALLOC_FREE(smb_fname_orig_lcomp); + goto out; + } + TALLOC_FREE(smb_fname_dst->stream_name); + smb_fname_dst->stream_name = tmp; } + TALLOC_FREE(fname_dst_lcomp_base_mod); + TALLOC_FREE(smb_fname_orig_lcomp); } /* @@ -5497,63 +5941,86 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, * don't do the rename, just return success. */ - if (strcsequal(fsp->fsp_name, newname)) { - DEBUG(3,("rename_internals_fsp: identical names in rename %s - returning success\n", - newname)); - return NT_STATUS_OK; + if (strcsequal(smb_fname_src->base_name, smb_fname_dst->base_name) && + strcsequal(smb_fname_src->stream_name, + smb_fname_dst->stream_name)) { + DEBUG(3, ("rename_internals_fsp: identical names in rename %s " + "- returning success\n", + smb_fname_str_dbg(smb_fname_dst))); + status = NT_STATUS_OK; + goto out; } - /* - * Have vfs_object_exist also fill sbuf1 - */ - dst_exists = vfs_object_exist(conn, newname, &sbuf1); + old_is_stream = is_ntfs_stream_smb_fname(smb_fname_src); + new_is_stream = is_ntfs_stream_smb_fname(smb_fname_dst); - if(!replace_if_exists && dst_exists) { - DEBUG(3,("rename_internals_fsp: dest exists doing rename %s -> %s\n", - fsp->fsp_name,newname)); - return NT_STATUS_OBJECT_NAME_COLLISION; + /* Return the correct error code if both names aren't streams. */ + if (!old_is_stream && new_is_stream) { + status = NT_STATUS_OBJECT_NAME_INVALID; + goto out; } - if(replace_if_exists && dst_exists) { - /* Ensure both or neither are stream names. */ - if (is_ntfs_stream_name(fsp->fsp_name) != - is_ntfs_stream_name(newname)) { - return NT_STATUS_INVALID_PARAMETER; - } + if (old_is_stream && !new_is_stream) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; } - if (dst_exists) { - struct file_id fileid = vfs_file_id_from_sbuf(conn, &sbuf1); + dst_exists = SMB_VFS_STAT(conn, smb_fname_dst) == 0; + + if(!replace_if_exists && dst_exists) { + DEBUG(3, ("rename_internals_fsp: dest exists doing rename " + "%s -> %s\n", smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); + status = NT_STATUS_OBJECT_NAME_COLLISION; + goto out; + } + + if (dst_exists) { + struct file_id fileid = vfs_file_id_from_sbuf(conn, + &smb_fname_dst->st); files_struct *dst_fsp = file_find_di_first(fileid); - if (dst_fsp) { + /* The file can be open when renaming a stream */ + if (dst_fsp && !new_is_stream) { DEBUG(3, ("rename_internals_fsp: Target file open\n")); - return NT_STATUS_ACCESS_DENIED; + status = NT_STATUS_ACCESS_DENIED; + goto out; } } /* Ensure we have a valid stat struct for the source. */ if (fsp->fh->fd != -1) { if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { - return map_nt_error_from_unix(errno); + status = map_nt_error_from_unix(errno); + goto out; } } else { - if (SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) == -1) { - return map_nt_error_from_unix(errno); + int ret = -1; + if (fsp->posix_open) { + ret = SMB_VFS_LSTAT(conn, smb_fname_src); + } else { + + ret = SMB_VFS_STAT(conn, smb_fname_src); } + if (ret == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } + sbuf = smb_fname_src->st; } status = can_rename(conn, fsp, attrs, &sbuf); if (!NT_STATUS_IS_OK(status)) { - DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n", - nt_errstr(status), fsp->fsp_name,newname)); + DEBUG(3, ("rename_internals_fsp: Error %s rename %s -> %s\n", + nt_errstr(status), smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) status = NT_STATUS_ACCESS_DENIED; - return status; + goto out; } - if (rename_path_prefix_equal(fsp->fsp_name, newname)) { - return NT_STATUS_ACCESS_DENIED; + if (rename_path_prefix_equal(smb_fname_src, smb_fname_dst)) { + status = NT_STATUS_ACCESS_DENIED; } lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, @@ -5566,15 +6033,17 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, SMB_ASSERT(lck != NULL); - if(SMB_VFS_RENAME(conn,fsp->fsp_name, newname) == 0) { + if(SMB_VFS_RENAME(conn, smb_fname_src, smb_fname_dst) == 0) { uint32 create_options = fsp->fh->private_options; - DEBUG(3,("rename_internals_fsp: succeeded doing rename on %s -> %s\n", - fsp->fsp_name,newname)); + DEBUG(3, ("rename_internals_fsp: succeeded doing rename on " + "%s -> %s\n", smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); - notify_rename(conn, fsp->is_directory, fsp->fsp_name, newname); + notify_rename(conn, fsp->is_directory, smb_fname_src, + smb_fname_dst); - rename_open_files(conn, lck, newname); + rename_open_files(conn, lck, smb_fname_dst); /* * A rename acts as a new file create w.r.t. allowing an initial delete @@ -5595,7 +6064,8 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, } } TALLOC_FREE(lck); - return NT_STATUS_OK; + status = NT_STATUS_OK; + goto out; } TALLOC_FREE(lck); @@ -5606,8 +6076,13 @@ NTSTATUS rename_internals_fsp(connection_struct *conn, status = map_nt_error_from_unix(errno); } - DEBUG(3,("rename_internals_fsp: Error %s rename %s -> %s\n", - nt_errstr(status), fsp->fsp_name,newname)); + DEBUG(3, ("rename_internals_fsp: Error %s rename %s -> %s\n", + nt_errstr(status), smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); + + out: + TALLOC_FREE(smb_fname_src); + TALLOC_FREE(smb_fname_dst); return status; } @@ -5628,34 +6103,29 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, bool dest_has_wild, uint32_t access_mask) { - char *directory = NULL; - char *mask = NULL; - char *last_component_src = NULL; - char *last_component_dest = NULL; - char *name = NULL; - char *newname = NULL; - char *p; + struct smb_filename *smb_fname_src = NULL; + struct smb_filename *smb_fname_dst = NULL; + char *fname_src_dir = NULL; + char *fname_src_mask = NULL; int count=0; NTSTATUS status = NT_STATUS_OK; - SMB_STRUCT_STAT sbuf1, sbuf2; struct smb_Dir *dir_hnd = NULL; const char *dname; long offset = 0; int create_options = 0; + bool posix_pathnames = lp_posix_pathnames(); - ZERO_STRUCT(sbuf1); - ZERO_STRUCT(sbuf2); - - status = unix_convert(ctx, conn, name_in, src_has_wild, &name, - &last_component_src, &sbuf1); + status = unix_convert(ctx, conn, name_in, &smb_fname_src, + src_has_wild ? UCF_ALLOW_WCARD_LCOMP : 0); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - status = unix_convert(ctx, conn, newname_in, dest_has_wild, &newname, - &last_component_dest, &sbuf2); + status = unix_convert(ctx, conn, newname_in, &smb_fname_dst, + (UCF_SAVE_LCOMP | + (dest_has_wild ? UCF_ALLOW_WCARD_LCOMP : 0))); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } /* @@ -5667,21 +6137,12 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, * as this is checked in resolve_wildcards(). */ - p = strrchr_m(name,'/'); - if (!p) { - directory = talloc_strdup(ctx, "."); - if (!directory) { - return NT_STATUS_NO_MEMORY; - } - mask = name; - } else { - *p = 0; - directory = talloc_strdup(ctx, name); - if (!directory) { - return NT_STATUS_NO_MEMORY; - } - mask = p+1; - *p = '/'; /* Replace needed for exceptional test below. */ + /* Split up the directory from the filename/mask. */ + status = split_fname_dir_mask(ctx, smb_fname_src->base_name, + &fname_src_dir, &fname_src_mask); + if (!NT_STATUS_IS_OK(status)) { + status = NT_STATUS_NO_MEMORY; + goto out; } /* @@ -5693,14 +6154,14 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, * Tine Smukavec . */ - if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) { + if (!VALID_STAT(smb_fname_src->st) && + mangle_is_mangled(fname_src_mask, conn->params)) { char *new_mask = NULL; - mangle_lookup_name_from_8_3(ctx, - mask, - &new_mask, - conn->params ); + mangle_lookup_name_from_8_3(ctx, fname_src_mask, &new_mask, + conn->params); if (new_mask) { - mask = new_mask; + TALLOC_FREE(fname_src_mask); + fname_src_mask = new_mask; } } @@ -5708,54 +6169,68 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, files_struct *fsp; /* - * No wildcards - just process the one file. + * Only one file needs to be renamed. Append the mask back + * onto the directory. */ - bool is_short_name = mangle_is_8_3(name, True, conn->params); - - /* Add a terminating '/' to the directory name. */ - directory = talloc_asprintf_append(directory, - "/%s", - mask); - if (!directory) { - return NT_STATUS_NO_MEMORY; - } - - /* Ensure newname contains a '/' also */ - if(strrchr_m(newname,'/') == 0) { - newname = talloc_asprintf(ctx, - "./%s", - newname); - if (!newname) { - return NT_STATUS_NO_MEMORY; + TALLOC_FREE(smb_fname_src->base_name); + smb_fname_src->base_name = talloc_asprintf(smb_fname_src, + "%s/%s", + fname_src_dir, + fname_src_mask); + if (!smb_fname_src->base_name) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + /* Ensure dst fname contains a '/' also */ + if(strrchr_m(smb_fname_dst->base_name, '/') == 0) { + char *tmp; + tmp = talloc_asprintf(smb_fname_dst, "./%s", + smb_fname_dst->base_name); + if (!tmp) { + status = NT_STATUS_NO_MEMORY; + goto out; } + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = tmp; } DEBUG(3, ("rename_internals: case_sensitive = %d, " "case_preserve = %d, short case preserve = %d, " "directory = %s, newname = %s, " - "last_component_dest = %s, is_8_3 = %d\n", + "last_component_dest = %s\n", conn->case_sensitive, conn->case_preserve, - conn->short_case_preserve, directory, - newname, last_component_dest, is_short_name)); + conn->short_case_preserve, + smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst), + smb_fname_dst->original_lcomp)); /* The dest name still may have wildcards. */ if (dest_has_wild) { - char *mod_newname = NULL; - if (!resolve_wildcards(ctx, - directory,newname,&mod_newname)) { + char *fname_dst_mod = NULL; + if (!resolve_wildcards(smb_fname_dst, + smb_fname_src->base_name, + smb_fname_dst->base_name, + &fname_dst_mod)) { DEBUG(6, ("rename_internals: resolve_wildcards " - "%s %s failed\n", - directory, - newname)); - return NT_STATUS_NO_MEMORY; + "%s %s failed\n", + smb_fname_src->base_name, + smb_fname_dst->base_name)); + status = NT_STATUS_NO_MEMORY; + goto out; } - newname = mod_newname; + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = fname_dst_mod; } - ZERO_STRUCT(sbuf1); - SMB_VFS_STAT(conn, directory, &sbuf1); + ZERO_STRUCT(smb_fname_src->st); + if (posix_pathnames) { + SMB_VFS_LSTAT(conn, smb_fname_src); + } else { + SMB_VFS_STAT(conn, smb_fname_src); + } - if (S_ISDIR(sbuf1.st_mode)) { + if (S_ISDIR(smb_fname_src->st.st_ex_mode)) { create_options |= FILE_DIRECTORY_FILE; } @@ -5763,56 +6238,61 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - directory, /* fname */ - 0, /* create_file_flags */ + smb_fname_src, /* fname */ access_mask, /* access_mask */ (FILE_SHARE_READ | /* share_access */ FILE_SHARE_WRITE), FILE_OPEN, /* create_disposition*/ create_options, /* create_options */ - 0, /* file_attributes */ + posix_pathnames ? FILE_FLAG_POSIX_SEMANTICS|0777 : 0, /* file_attributes */ 0, /* oplock_request */ 0, /* allocation_size */ NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - &sbuf1); /* psbuf */ + NULL); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("Could not open rename source %s: %s\n", - directory, nt_errstr(status))); - return status; + smb_fname_str_dbg(smb_fname_src), + nt_errstr(status))); + goto out; } - status = rename_internals_fsp(conn, fsp, newname, - last_component_dest, + status = rename_internals_fsp(conn, fsp, smb_fname_dst, attrs, replace_if_exists); close_file(req, fsp, NORMAL_CLOSE); DEBUG(3, ("rename_internals: Error %s rename %s -> %s\n", - nt_errstr(status), directory,newname)); + nt_errstr(status), smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); - return status; + goto out; } /* * Wildcards - process each file that matches. */ - if (strequal(mask,"????????.???")) { - mask[0] = '*'; - mask[1] = '\0'; + if (strequal(fname_src_mask, "????????.???")) { + TALLOC_FREE(fname_src_mask); + fname_src_mask = talloc_strdup(ctx, "*"); + if (!fname_src_mask) { + status = NT_STATUS_NO_MEMORY; + goto out; + } } - status = check_name(conn, directory); + status = check_name(conn, fname_src_dir); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, attrs); + dir_hnd = OpenDir(talloc_tos(), conn, fname_src_dir, fname_src_mask, + attrs); if (dir_hnd == NULL) { - return map_nt_error_from_unix(errno); + status = map_nt_error_from_unix(errno); + goto out; } status = NT_STATUS_NO_SUCH_FILE; @@ -5821,9 +6301,8 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, * - gentest fix. JRA */ - while ((dname = ReadDirName(dir_hnd, &offset))) { + while ((dname = ReadDirName(dir_hnd, &offset, &smb_fname_src->st))) { files_struct *fsp = NULL; - char *fname = NULL; char *destname = NULL; bool sysdir_entry = False; @@ -5836,11 +6315,12 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, } } - if (!is_visible_file(conn, directory, dname, &sbuf1, False)) { + if (!is_visible_file(conn, fname_src_dir, dname, + &smb_fname_src->st, false)) { continue; } - if(!mask_match(dname, mask, conn->case_sensitive)) { + if(!mask_match(dname, fname_src_mask, conn->case_sensitive)) { continue; } @@ -5849,31 +6329,41 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, break; } - fname = talloc_asprintf(ctx, - "%s/%s", - directory, - dname); - if (!fname) { - return NT_STATUS_NO_MEMORY; + TALLOC_FREE(smb_fname_src->base_name); + smb_fname_src->base_name = talloc_asprintf(smb_fname_src, + "%s/%s", + fname_src_dir, + dname); + if (!smb_fname_src->base_name) { + status = NT_STATUS_NO_MEMORY; + goto out; } - if (!resolve_wildcards(ctx, - fname,newname,&destname)) { + if (!resolve_wildcards(ctx, smb_fname_src->base_name, + smb_fname_dst->base_name, + &destname)) { DEBUG(6, ("resolve_wildcards %s %s failed\n", - fname, destname)); - TALLOC_FREE(fname); + smb_fname_src->base_name, destname)); continue; } if (!destname) { - return NT_STATUS_NO_MEMORY; + status = NT_STATUS_NO_MEMORY; + goto out; } - ZERO_STRUCT(sbuf1); - SMB_VFS_STAT(conn, fname, &sbuf1); + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = destname; + + ZERO_STRUCT(smb_fname_src->st); + if (posix_pathnames) { + SMB_VFS_LSTAT(conn, smb_fname_src); + } else { + SMB_VFS_STAT(conn, smb_fname_src); + } create_options = 0; - if (S_ISDIR(sbuf1.st_mode)) { + if (S_ISDIR(smb_fname_src->st.st_ex_mode)) { create_options |= FILE_DIRECTORY_FILE; } @@ -5881,30 +6371,37 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, conn, /* conn */ req, /* req */ 0, /* root_dir_fid */ - fname, /* fname */ - 0, /* create_file_flags */ + smb_fname_src, /* fname */ access_mask, /* access_mask */ (FILE_SHARE_READ | /* share_access */ FILE_SHARE_WRITE), FILE_OPEN, /* create_disposition*/ create_options, /* create_options */ - 0, /* file_attributes */ + posix_pathnames ? FILE_FLAG_POSIX_SEMANTICS|0777 : 0, /* file_attributes */ 0, /* oplock_request */ 0, /* allocation_size */ NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - &sbuf1); /* psbuf */ + NULL); /* pinfo */ if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("rename_internals: SMB_VFS_CREATE_FILE " "returned %s rename %s -> %s\n", - nt_errstr(status), directory, newname)); + nt_errstr(status), + smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); break; } - status = rename_internals_fsp(conn, fsp, destname, dname, + smb_fname_dst->original_lcomp = talloc_strdup(smb_fname_dst, + dname); + if (!smb_fname_dst->original_lcomp) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + status = rename_internals_fsp(conn, fsp, smb_fname_dst, attrs, replace_if_exists); close_file(req, fsp, NORMAL_CLOSE); @@ -5912,17 +6409,17 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("rename_internals_fsp returned %s for " "rename %s -> %s\n", nt_errstr(status), - directory, newname)); + smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_dst))); break; } count++; DEBUG(3,("rename_internals: doing rename on %s -> " - "%s\n",fname,destname)); + "%s\n", smb_fname_str_dbg(smb_fname_src), + smb_fname_str_dbg(smb_fname_src))); - TALLOC_FREE(fname); - TALLOC_FREE(destname); } TALLOC_FREE(dir_hnd); @@ -5930,6 +6427,11 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx, status = map_nt_error_from_unix(errno); } + out: + TALLOC_FREE(smb_fname_src); + TALLOC_FREE(smb_fname_dst); + TALLOC_FREE(fname_src_dir); + TALLOC_FREE(fname_src_mask); return status; } @@ -6041,60 +6543,74 @@ void reply_mv(struct smb_request *req) NTSTATUS copy_file(TALLOC_CTX *ctx, connection_struct *conn, - const char *src, - const char *dest1, + struct smb_filename *smb_fname_src, + struct smb_filename *smb_fname_dst, int ofun, int count, bool target_is_directory) { - SMB_STRUCT_STAT src_sbuf, sbuf2; + struct smb_filename *smb_fname_dst_tmp = NULL; + char *fname_src = NULL; SMB_OFF_T ret=-1; files_struct *fsp1,*fsp2; - char *dest = NULL; uint32 dosattrs; uint32 new_create_disposition; NTSTATUS status; - dest = talloc_strdup(ctx, dest1); - if (!dest) { - return NT_STATUS_NO_MEMORY; + + status = copy_smb_filename(ctx, smb_fname_dst, &smb_fname_dst_tmp); + if (!NT_STATUS_IS_OK(status)) { + return status; } + + /* + * If the target is a directory, extract the last component from the + * src filename and append it to the dst filename + */ if (target_is_directory) { - const char *p = strrchr_m(src,'/'); + const char *p; + + /* dest/target can't be a stream if it's a directory. */ + SMB_ASSERT(smb_fname_dst->stream_name == NULL); + + p = strrchr_m(smb_fname_src->base_name,'/'); if (p) { p++; } else { - p = src; + p = smb_fname_src->base_name; } - dest = talloc_asprintf_append(dest, - "/%s", - p); - if (!dest) { - return NT_STATUS_NO_MEMORY; + smb_fname_dst_tmp->base_name = + talloc_asprintf_append(smb_fname_dst_tmp->base_name, "/%s", + p); + if (!smb_fname_dst_tmp->base_name) { + status = NT_STATUS_NO_MEMORY; + goto out; } } - if (!vfs_file_exist(conn,src,&src_sbuf)) { - TALLOC_FREE(dest); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + status = vfs_file_exist(conn, smb_fname_src); + if (!NT_STATUS_IS_OK(status)) { + goto out; } if (!target_is_directory && count) { new_create_disposition = FILE_OPEN; } else { - if (!map_open_params_to_ntcreate(dest1,0,ofun, - NULL, NULL, &new_create_disposition, NULL)) { - TALLOC_FREE(dest); - return NT_STATUS_INVALID_PARAMETER; + if (!map_open_params_to_ntcreate(smb_fname_dst_tmp->base_name, + 0, ofun, NULL, NULL, + &new_create_disposition, + NULL)) { + status = NT_STATUS_INVALID_PARAMETER; + goto out; } } + /* Open the src file for reading. */ status = SMB_VFS_CREATE_FILE( conn, /* conn */ NULL, /* req */ 0, /* root_dir_fid */ - src, /* fname */ - 0, /* create_file_flags */ + smb_fname_src, /* fname */ FILE_GENERIC_READ, /* access_mask */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ FILE_OPEN, /* create_disposition*/ @@ -6105,25 +6621,31 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, NULL, /* sd */ NULL, /* ea_list */ &fsp1, /* result */ - NULL, /* pinfo */ - &src_sbuf); /* psbuf */ + NULL); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(dest); - return status; + goto out; } - dosattrs = dos_mode(conn, src, &src_sbuf); - if (SMB_VFS_STAT(conn,dest,&sbuf2) == -1) { - ZERO_STRUCTP(&sbuf2); + status = get_full_smb_filename(talloc_tos(), smb_fname_src, &fname_src); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + dosattrs = dos_mode(conn, fname_src, &smb_fname_src->st); + + TALLOC_FREE(fname_src); + + if (SMB_VFS_STAT(conn, smb_fname_dst_tmp) == -1) { + ZERO_STRUCTP(&smb_fname_dst_tmp->st); } + /* Open the dst file for writing. */ status = SMB_VFS_CREATE_FILE( conn, /* conn */ NULL, /* req */ 0, /* root_dir_fid */ - dest, /* fname */ - 0, /* create_file_flags */ + smb_fname_dst, /* fname */ FILE_GENERIC_WRITE, /* access_mask */ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */ new_create_disposition, /* create_disposition*/ @@ -6134,14 +6656,11 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, NULL, /* sd */ NULL, /* ea_list */ &fsp2, /* result */ - NULL, /* pinfo */ - &sbuf2); /* psbuf */ - - TALLOC_FREE(dest); + NULL); /* psbuf */ if (!NT_STATUS_IS_OK(status)) { close_file(NULL, fsp1, ERROR_CLOSE); - return status; + goto out; } if ((ofun&3) == 1) { @@ -6151,18 +6670,19 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, * Stop the copy from occurring. */ ret = -1; - src_sbuf.st_size = 0; + smb_fname_src->st.st_ex_size = 0; } } - if (src_sbuf.st_size) { - ret = vfs_transfer_file(fsp1, fsp2, src_sbuf.st_size); + /* Do the actual copy. */ + if (smb_fname_src->st.st_ex_size) { + ret = vfs_transfer_file(fsp1, fsp2, smb_fname_src->st.st_ex_size); } close_file(NULL, fsp1, NORMAL_CLOSE); /* Ensure the modtime is set correctly on the destination file. */ - set_close_write_time(fsp2, get_mtimespec(&src_sbuf)); + set_close_write_time(fsp2, smb_fname_src->st.st_ex_mtime); /* * As we are opening fsp1 read-only we only expect @@ -6173,14 +6693,19 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, status = close_file(NULL, fsp2, NORMAL_CLOSE); if (!NT_STATUS_IS_OK(status)) { - return status; + goto out; } - if (ret != (SMB_OFF_T)src_sbuf.st_size) { - return NT_STATUS_DISK_FULL; + if (ret != (SMB_OFF_T)smb_fname_src->st.st_ex_size) { + status = NT_STATUS_DISK_FULL; + goto out; } - return NT_STATUS_OK; + status = NT_STATUS_OK; + + out: + TALLOC_FREE(smb_fname_dst_tmp); + return status; } /**************************************************************************** @@ -6190,11 +6715,12 @@ NTSTATUS copy_file(TALLOC_CTX *ctx, void reply_copy(struct smb_request *req) { connection_struct *conn = req->conn; - char *name = NULL; - char *newname = NULL; - char *directory = NULL; - const char *mask = NULL; - const char mask_star[] = "*"; + struct smb_filename *smb_fname_src = NULL; + struct smb_filename *smb_fname_dst = NULL; + char *fname_src = NULL; + char *fname_dst = NULL; + char *fname_src_mask = NULL; + char *fname_src_dir = NULL; const char *p; int count=0; int error = ERRnoaccess; @@ -6205,7 +6731,6 @@ void reply_copy(struct smb_request *req) bool target_is_directory=False; bool source_has_wild = False; bool dest_has_wild = False; - SMB_STRUCT_STAT sbuf1, sbuf2; NTSTATUS status; TALLOC_CTX *ctx = talloc_tos(); @@ -6213,8 +6738,7 @@ void reply_copy(struct smb_request *req) if (req->wct < 3) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBcopy); - return; + goto out; } tid2 = SVAL(req->vwv+0, 0); @@ -6222,116 +6746,97 @@ void reply_copy(struct smb_request *req) flags = SVAL(req->vwv+2, 0); p = (const char *)req->buf; - p += srvstr_get_path_req_wcard(ctx, req, &name, p, STR_TERMINATE, + p += srvstr_get_path_req_wcard(ctx, req, &fname_src, p, STR_TERMINATE, &status, &source_has_wild); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - p += srvstr_get_path_req_wcard(ctx, req, &newname, p, STR_TERMINATE, + p += srvstr_get_path_req_wcard(ctx, req, &fname_dst, p, STR_TERMINATE, &status, &dest_has_wild); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); + DEBUG(3,("reply_copy : %s -> %s\n", fname_src, fname_dst)); if (tid2 != conn->cnum) { /* can't currently handle inter share copies XXXX */ DEBUG(3,("Rejecting inter-share copy\n")); reply_doserror(req, ERRSRV, ERRinvdevice); - END_PROFILE(SMBcopy); - return; + goto out; } status = resolve_dfspath_wcard(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, - name, - &name, + fname_src, + &fname_src, &source_has_wild); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBcopy); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } status = resolve_dfspath_wcard(ctx, conn, req->flags2 & FLAGS2_DFS_PATHNAMES, - newname, - &newname, + fname_dst, + &fname_dst, &dest_has_wild); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) { reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath); - END_PROFILE(SMBcopy); - return; + goto out; } reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - status = unix_convert(ctx, conn, name, source_has_wild, - &name, NULL, &sbuf1); + status = unix_convert(ctx, conn, fname_src, &smb_fname_src, + source_has_wild ? UCF_ALLOW_WCARD_LCOMP : 0); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - status = unix_convert(ctx, conn, newname, dest_has_wild, - &newname, NULL, &sbuf2); + status = unix_convert(ctx, conn, fname_dst, &smb_fname_dst, + dest_has_wild ? UCF_ALLOW_WCARD_LCOMP : 0); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - target_is_directory = VALID_STAT_OF_DIR(sbuf2); + target_is_directory = VALID_STAT_OF_DIR(smb_fname_dst->st); if ((flags&1) && target_is_directory) { reply_doserror(req, ERRDOS, ERRbadfile); - END_PROFILE(SMBcopy); - return; + goto out; } if ((flags&2) && !target_is_directory) { reply_doserror(req, ERRDOS, ERRbadpath); - END_PROFILE(SMBcopy); - return; + goto out; } - if ((flags&(1<<5)) && VALID_STAT_OF_DIR(sbuf1)) { + if ((flags&(1<<5)) && VALID_STAT_OF_DIR(smb_fname_src->st)) { /* wants a tree copy! XXXX */ DEBUG(3,("Rejecting tree copy\n")); reply_doserror(req, ERRSRV, ERRerror); - END_PROFILE(SMBcopy); - return; + goto out; } - p = strrchr_m(name,'/'); - if (p != NULL) { - directory = talloc_strndup(ctx, name, PTR_DIFF(p, name)); - mask = p+1; - } else { - directory = talloc_strdup(ctx, "./"); - mask = name; - } - - if (!directory) { + /* Split up the directory from the filename/mask. */ + status = split_fname_dir_mask(ctx, smb_fname_src->base_name, + &fname_src_dir, &fname_src_mask); + if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBcopy); - return; + goto out; } /* @@ -6342,54 +6847,66 @@ void reply_copy(struct smb_request *req) * for a possible mangle. This patch from * Tine Smukavec . */ - - if (!VALID_STAT(sbuf1) && mangle_is_mangled(mask, conn->params)) { + if (!VALID_STAT(smb_fname_src->st) && + mangle_is_mangled(fname_src_mask, conn->params)) { char *new_mask = NULL; - mangle_lookup_name_from_8_3(ctx, - mask, - &new_mask, - conn->params ); + mangle_lookup_name_from_8_3(ctx, fname_src_mask, + &new_mask, conn->params); + + /* Use demangled name if one was successfully found. */ if (new_mask) { - mask = new_mask; + TALLOC_FREE(fname_src_mask); + fname_src_mask = new_mask; } } if (!source_has_wild) { - directory = talloc_asprintf_append(directory, - "/%s", - mask); + + /* + * Only one file needs to be copied. Append the mask back onto + * the directory. + */ + TALLOC_FREE(smb_fname_src->base_name); + smb_fname_src->base_name = talloc_asprintf(smb_fname_src, + "%s/%s", + fname_src_dir, + fname_src_mask); + if (!smb_fname_src->base_name) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } + if (dest_has_wild) { - char *mod_newname = NULL; - if (!resolve_wildcards(ctx, - directory,newname,&mod_newname)) { + char *fname_dst_mod = NULL; + if (!resolve_wildcards(smb_fname_dst, + smb_fname_src->base_name, + smb_fname_dst->base_name, + &fname_dst_mod)) { reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBcopy); - return; + goto out; } - newname = mod_newname; + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = fname_dst_mod; } - status = check_name(conn, directory); + status = check_name(conn, smb_fname_src->base_name); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - status = check_name(conn, newname); + status = check_name(conn, smb_fname_dst->base_name); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - status = copy_file(ctx,conn,directory,newname,ofun, - count,target_is_directory); + status = copy_file(ctx, conn, smb_fname_src, smb_fname_dst, + ofun, count, target_is_directory); if(!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } else { count++; } @@ -6398,91 +6915,113 @@ void reply_copy(struct smb_request *req) const char *dname = NULL; long offset = 0; - if (strequal(mask,"????????.???")) { - mask = mask_star; + /* + * There is a wildcard that requires us to actually read the + * src dir and copy each file matching the mask to the dst. + * Right now streams won't be copied, but this could + * presumably be added with a nested loop for reach dir entry. + */ + SMB_ASSERT(!smb_fname_src->stream_name); + SMB_ASSERT(!smb_fname_dst->stream_name); + + smb_fname_src->stream_name = NULL; + smb_fname_dst->stream_name = NULL; + + if (strequal(fname_src_mask,"????????.???")) { + TALLOC_FREE(fname_src_mask); + fname_src_mask = talloc_strdup(ctx, "*"); + if (!fname_src_mask) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + goto out; + } } - status = check_name(conn, directory); + status = check_name(conn, fname_src_dir); if (!NT_STATUS_IS_OK(status)) { reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - dir_hnd = OpenDir(talloc_tos(), conn, directory, mask, 0); + dir_hnd = OpenDir(ctx, conn, fname_src_dir, fname_src_mask, 0); if (dir_hnd == NULL) { status = map_nt_error_from_unix(errno); reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } error = ERRbadfile; - while ((dname = ReadDirName(dir_hnd, &offset))) { + /* Iterate over the src dir copying each entry to the dst. */ + while ((dname = ReadDirName(dir_hnd, &offset, + &smb_fname_src->st))) { char *destname = NULL; - char *fname = NULL; if (ISDOT(dname) || ISDOTDOT(dname)) { continue; } - if (!is_visible_file(conn, directory, dname, &sbuf1, False)) { + if (!is_visible_file(conn, fname_src_dir, dname, + &smb_fname_src->st, false)) { continue; } - if(!mask_match(dname, mask, conn->case_sensitive)) { + if(!mask_match(dname, fname_src_mask, + conn->case_sensitive)) { continue; } error = ERRnoaccess; - fname = talloc_asprintf(ctx, - "%s/%s", - directory, - dname); - if (!fname) { + + /* Get the src smb_fname struct setup. */ + TALLOC_FREE(smb_fname_src->base_name); + smb_fname_src->base_name = + talloc_asprintf(smb_fname_src, "%s/%s", + fname_src_dir, dname); + + if (!smb_fname_src->base_name) { TALLOC_FREE(dir_hnd); reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBcopy); - return; + goto out; } - if (!resolve_wildcards(ctx, - fname,newname,&destname)) { + if (!resolve_wildcards(ctx, smb_fname_src->base_name, + smb_fname_dst->base_name, + &destname)) { continue; } if (!destname) { TALLOC_FREE(dir_hnd); reply_nterror(req, NT_STATUS_NO_MEMORY); - END_PROFILE(SMBcopy); - return; + goto out; } - status = check_name(conn, fname); + TALLOC_FREE(smb_fname_dst->base_name); + smb_fname_dst->base_name = destname; + + status = check_name(conn, smb_fname_src->base_name); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(dir_hnd); reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - status = check_name(conn, destname); + status = check_name(conn, smb_fname_dst->base_name); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(dir_hnd); reply_nterror(req, status); - END_PROFILE(SMBcopy); - return; + goto out; } - DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname, destname)); + DEBUG(3,("reply_copy : doing copy on %s -> %s\n", + smb_fname_src->base_name, + smb_fname_dst->base_name)); - status = copy_file(ctx,conn,fname,destname,ofun, - count,target_is_directory); + status = copy_file(ctx, conn, smb_fname_src, + smb_fname_dst, ofun, count, + target_is_directory); if (NT_STATUS_IS_OK(status)) { count++; } - TALLOC_FREE(fname); - TALLOC_FREE(destname); } TALLOC_FREE(dir_hnd); } @@ -6492,17 +7031,22 @@ void reply_copy(struct smb_request *req) /* Error on close... */ errno = err; reply_unixerror(req, ERRHRD, ERRgeneral); - END_PROFILE(SMBcopy); - return; + goto out; } reply_doserror(req, ERRDOS, error); - END_PROFILE(SMBcopy); - return; + goto out; } reply_outbuf(req, 1, 0); SSVAL(req->outbuf,smb_vwv0,count); + out: + TALLOC_FREE(smb_fname_src); + TALLOC_FREE(smb_fname_dst); + TALLOC_FREE(fname_src); + TALLOC_FREE(fname_dst); + TALLOC_FREE(fname_src_mask); + TALLOC_FREE(fname_src_dir); END_PROFILE(SMBcopy); return; @@ -6647,6 +7191,208 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset, return offset; } +struct smbd_lock_element { + uint32_t smbpid; + uint64_t offset; + uint64_t count; +}; + +static NTSTATUS smbd_do_locking(struct smb_request *req, + files_struct *fsp, + uint8_t type, + int32_t timeout, + uint16_t num_ulocks, + struct smbd_lock_element *ulocks, + uint16_t num_locks, + struct smbd_lock_element *locks, + bool *async) +{ + connection_struct *conn = req->conn; + int i; + NTSTATUS status = NT_STATUS_OK; + + *async = false; + + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + struct smbd_lock_element *e = &ulocks[i]; + + DEBUG(10,("smbd_do_locking: unlock start=%.0f, len=%.0f for " + "pid %u, file %s\n", + (double)e->offset, + (double)e->count, + (unsigned int)e->smbpid, + fsp->fsp_name)); + + status = do_unlock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK); + + DEBUG(10, ("smbd_do_locking: unlock returned %s\n", + nt_errstr(status))); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* Setup the timeout in seconds. */ + + if (!lp_blocking_locks(SNUM(conn))) { + timeout = 0; + } + + /* Data now points at the beginning of the list + of smb_lkrng structs */ + + for(i = 0; i < (int)num_locks; i++) { + struct smbd_lock_element *e = &locks[i]; + enum brl_type brltype = ((type & LOCKING_ANDX_SHARED_LOCK) ? + READ_LOCK:WRITE_LOCK); + + DEBUG(10,("smbd_do_locking: lock start=%.0f, len=%.0f for pid " + "%u, file %s timeout = %d\n", + (double)e->offset, + (double)e->count, + (unsigned int)e->smbpid, + fsp->fsp_name, + (int)timeout)); + + if (type & LOCKING_ANDX_CANCEL_LOCK) { + struct blocking_lock_record *blr = NULL; + + if (lp_blocking_locks(SNUM(conn))) { + + /* Schedule a message to ourselves to + remove the blocking lock record and + return the right error. */ + + blr = blocking_lock_cancel(fsp, + e->smbpid, + e->offset, + e->count, + WINDOWS_LOCK, + type, + NT_STATUS_FILE_LOCK_CONFLICT); + if (blr == NULL) { + return NT_STATUS_DOS( + ERRDOS, + ERRcancelviolation); + } + } + /* Remove a matching pending lock. */ + status = do_lock_cancel(fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK, + blr); + } else { + bool blocking_lock = timeout ? true : false; + bool defer_lock = false; + struct byte_range_lock *br_lck; + uint32_t block_smbpid; + + br_lck = do_lock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + brltype, + WINDOWS_LOCK, + blocking_lock, + &status, + &block_smbpid, + NULL); + + if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { + /* Windows internal resolution for blocking locks seems + to be about 200ms... Don't wait for less than that. JRA. */ + if (timeout != -1 && timeout < lp_lock_spin_time()) { + timeout = lp_lock_spin_time(); + } + defer_lock = true; + } + + /* This heuristic seems to match W2K3 very well. If a + lock sent with timeout of zero would fail with NT_STATUS_FILE_LOCK_CONFLICT + it pretends we asked for a timeout of between 150 - 300 milliseconds as + far as I can tell. Replacement for do_lock_spin(). JRA. */ + + if (br_lck && lp_blocking_locks(SNUM(conn)) && !blocking_lock && + NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT)) { + defer_lock = true; + timeout = lp_lock_spin_time(); + } + + if (br_lck && defer_lock) { + /* + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. + */ + if(push_blocking_lock_request(br_lck, + req, + fsp, + timeout, + i, + e->smbpid, + brltype, + WINDOWS_LOCK, + e->offset, + e->count, + block_smbpid)) { + TALLOC_FREE(br_lck); + *async = true; + return NT_STATUS_OK; + } + } + + TALLOC_FREE(br_lck); + } + + if (!NT_STATUS_IS_OK(status)) { + break; + } + } + + /* If any of the above locks failed, then we must unlock + all of the previous locks (X/Open spec). */ + + if (num_locks != 0 && !NT_STATUS_IS_OK(status)) { + + if (type & LOCKING_ANDX_CANCEL_LOCK) { + i = -1; /* we want to skip the for loop */ + } + + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + for(i--; i >= 0; i--) { + struct smbd_lock_element *e = &locks[i]; + + do_unlock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK); + } + return status; + } + + DEBUG(3, ("smbd_do_locking: fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)type, num_locks, num_ulocks)); + + return NT_STATUS_OK; +} + /**************************************************************************** Reply to a lockingX request. ****************************************************************************/ @@ -6659,14 +7405,15 @@ void reply_lockingX(struct smb_request *req) unsigned char oplocklevel; uint16 num_ulocks; uint16 num_locks; - uint64_t count = 0, offset = 0; - uint32 lock_pid; int32 lock_timeout; int i; const uint8_t *data; bool large_file_format; bool err; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + struct smbd_lock_element *ulocks; + struct smbd_lock_element *locks; + bool async = false; START_PROFILE(SMBlockingX); @@ -6772,13 +7519,6 @@ void reply_lockingX(struct smb_request *req) } } - /* - * We do this check *after* we have checked this is not a oplock break - * response message. JRA. - */ - - release_level_2_oplocks_on_change(fsp); - if (req->buflen < (num_ulocks + num_locks) * (large_file_format ? 20 : 10)) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); @@ -6786,12 +7526,26 @@ void reply_lockingX(struct smb_request *req) return; } + ulocks = talloc_array(req, struct smbd_lock_element, num_ulocks); + if (ulocks == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBlockingX); + return; + } + + locks = talloc_array(req, struct smbd_lock_element, num_locks); + if (locks == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBlockingX); + return; + } + /* Data now points at the beginning of the list of smb_unlkrng structs */ for(i = 0; i < (int)num_ulocks; i++) { - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, &err); + ulocks[i].smbpid = get_lock_pid(data, i, large_file_format); + ulocks[i].count = get_lock_count(data, i, large_file_format); + ulocks[i].offset = get_lock_offset(data, i, large_file_format, &err); /* * There is no error code marked "stupid client bug".... :-). @@ -6801,29 +7555,6 @@ void reply_lockingX(struct smb_request *req) reply_doserror(req, ERRDOS, ERRnoaccess); return; } - - DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for " - "pid %u, file %s\n", (double)offset, (double)count, - (unsigned int)lock_pid, fsp->fsp_name )); - - status = do_unlock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK); - - if (NT_STATUS_V(status)) { - END_PROFILE(SMBlockingX); - reply_nterror(req, status); - return; - } - } - - /* Setup the timeout in seconds. */ - - if (!lp_blocking_locks(SNUM(conn))) { - lock_timeout = 0; } /* Now do any requested locks */ @@ -6833,11 +7564,9 @@ void reply_lockingX(struct smb_request *req) of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { - enum brl_type lock_type = ((locktype & LOCKING_ANDX_SHARED_LOCK) ? - READ_LOCK:WRITE_LOCK); - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, &err); + locks[i].smbpid = get_lock_pid(data, i, large_file_format); + locks[i].count = get_lock_count(data, i, large_file_format); + locks[i].offset = get_lock_offset(data, i, large_file_format, &err); /* * There is no error code marked "stupid client bug".... :-). @@ -6847,149 +7576,22 @@ void reply_lockingX(struct smb_request *req) reply_doserror(req, ERRDOS, ERRnoaccess); return; } - - DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid " - "%u, file %s timeout = %d\n", (double)offset, - (double)count, (unsigned int)lock_pid, - fsp->fsp_name, (int)lock_timeout )); - - if (locktype & LOCKING_ANDX_CANCEL_LOCK) { - if (lp_blocking_locks(SNUM(conn))) { - - /* Schedule a message to ourselves to - remove the blocking lock record and - return the right error. */ - - if (!blocking_lock_cancel(fsp, - lock_pid, - offset, - count, - WINDOWS_LOCK, - locktype, - NT_STATUS_FILE_LOCK_CONFLICT)) { - END_PROFILE(SMBlockingX); - reply_nterror( - req, - NT_STATUS_DOS( - ERRDOS, - ERRcancelviolation)); - return; - } - } - /* Remove a matching pending lock. */ - status = do_lock_cancel(fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK); - } else { - bool blocking_lock = lock_timeout ? True : False; - bool defer_lock = False; - struct byte_range_lock *br_lck; - uint32 block_smbpid; - - br_lck = do_lock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - lock_type, - WINDOWS_LOCK, - blocking_lock, - &status, - &block_smbpid); - - if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { - /* Windows internal resolution for blocking locks seems - to be about 200ms... Don't wait for less than that. JRA. */ - if (lock_timeout != -1 && lock_timeout < lp_lock_spin_time()) { - lock_timeout = lp_lock_spin_time(); - } - defer_lock = True; - } - - /* This heuristic seems to match W2K3 very well. If a - lock sent with timeout of zero would fail with NT_STATUS_FILE_LOCK_CONFLICT - it pretends we asked for a timeout of between 150 - 300 milliseconds as - far as I can tell. Replacement for do_lock_spin(). JRA. */ - - if (br_lck && lp_blocking_locks(SNUM(conn)) && !blocking_lock && - NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT)) { - defer_lock = True; - lock_timeout = lp_lock_spin_time(); - } - - if (br_lck && defer_lock) { - /* - * A blocking lock was requested. Package up - * this smb into a queued request and push it - * onto the blocking lock queue. - */ - if(push_blocking_lock_request(br_lck, - req, - fsp, - lock_timeout, - i, - lock_pid, - lock_type, - WINDOWS_LOCK, - offset, - count, - block_smbpid)) { - TALLOC_FREE(br_lck); - END_PROFILE(SMBlockingX); - return; - } - } - - TALLOC_FREE(br_lck); - } - - if (NT_STATUS_V(status)) { - END_PROFILE(SMBlockingX); - reply_nterror(req, status); - return; - } } - /* If any of the above locks failed, then we must unlock - all of the previous locks (X/Open spec). */ - - if (!(locktype & LOCKING_ANDX_CANCEL_LOCK) && - (i != num_locks) && - (num_locks != 0)) { - /* - * Ensure we don't do a remove on the lock that just failed, - * as under POSIX rules, if we have a lock already there, we - * will delete it (and we shouldn't) ..... - */ - for(i--; i >= 0; i--) { - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, - &err); - - /* - * There is no error code marked "stupid client - * bug".... :-). - */ - if(err) { - END_PROFILE(SMBlockingX); - reply_doserror(req, ERRDOS, ERRnoaccess); - return; - } - - do_unlock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK); - } + status = smbd_do_locking(req, fsp, + locktype, lock_timeout, + num_ulocks, ulocks, + num_locks, locks, + &async); + if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBlockingX); reply_nterror(req, status); return; } + if (async) { + END_PROFILE(SMBlockingX); + return; + } reply_outbuf(req, 2, 0); @@ -7038,37 +7640,44 @@ void reply_readbs(struct smb_request *req) void reply_setattrE(struct smb_request *req) { connection_struct *conn = req->conn; - struct timespec ts[2]; + struct smb_filename *smb_fname = NULL; + struct smb_file_time ft; files_struct *fsp; - SMB_STRUCT_STAT sbuf; NTSTATUS status; START_PROFILE(SMBsetattrE); + ZERO_STRUCT(ft); if (req->wct < 7) { reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - END_PROFILE(SMBsetattrE); - return; + goto out; } fsp = file_fsp(req, SVAL(req->vwv+0, 0)); if(!fsp || (fsp->conn != conn)) { reply_doserror(req, ERRDOS, ERRbadfid); - END_PROFILE(SMBsetattrE); - return; + goto out; } + /* XXX: Remove when fsp->fsp_name is converted to smb_filename. */ + status = create_synthetic_smb_fname_split(talloc_tos(), fsp->fsp_name, + NULL, &smb_fname); + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + goto out; + } /* - * Convert the DOS times into unix times. Ignore create - * time as UNIX can't set this. + * Convert the DOS times into unix times. */ - ts[0] = convert_time_t_to_timespec( - srv_make_unix_date2(req->vwv+3)); /* atime. */ - ts[1] = convert_time_t_to_timespec( - srv_make_unix_date2(req->vwv+5)); /* mtime. */ + ft.atime = convert_time_t_to_timespec( + srv_make_unix_date2(req->vwv+3)); + ft.mtime = convert_time_t_to_timespec( + srv_make_unix_date2(req->vwv+5)); + ft.create_time = convert_time_t_to_timespec( + srv_make_unix_date2(req->vwv+1)); reply_outbuf(req, 0, 0); @@ -7079,34 +7688,40 @@ void reply_setattrE(struct smb_request *req) /* Ensure we have a valid stat struct for the source. */ if (fsp->fh->fd != -1) { - if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) { + if (SMB_VFS_FSTAT(fsp, &smb_fname->st) == -1) { status = map_nt_error_from_unix(errno); reply_nterror(req, status); - END_PROFILE(SMBsetattrE); - return; + goto out; } } else { - if (SMB_VFS_STAT(conn, fsp->fsp_name, &sbuf) == -1) { + int ret = -1; + + if (fsp->posix_open) { + ret = SMB_VFS_LSTAT(conn, smb_fname); + } else { + ret = SMB_VFS_STAT(conn, smb_fname); + } + if (ret == -1) { status = map_nt_error_from_unix(errno); reply_nterror(req, status); - END_PROFILE(SMBsetattrE); - return; + goto out; } } - status = smb_set_file_time(conn, fsp, fsp->fsp_name, - &sbuf, ts, true); + status = smb_set_file_time(conn, fsp, smb_fname, &ft, true); if (!NT_STATUS_IS_OK(status)) { reply_doserror(req, ERRDOS, ERRnoaccess); - END_PROFILE(SMBsetattrE); - return; + goto out; } - DEBUG( 3, ( "reply_setattrE fnum=%d actime=%u modtime=%u\n", + DEBUG( 3, ( "reply_setattrE fnum=%d actime=%u modtime=%u " + " createtime=%u\n", fsp->fnum, - (unsigned int)ts[0].tv_sec, - (unsigned int)ts[1].tv_sec)); - + (unsigned int)ft.atime.tv_sec, + (unsigned int)ft.mtime.tv_sec, + (unsigned int)ft.create_time.tv_sec + )); + out: END_PROFILE(SMBsetattrE); return; } @@ -7187,19 +7802,20 @@ void reply_getattrE(struct smb_request *req) reply_outbuf(req, 11, 0); - create_ts = get_create_timespec(&sbuf, - lp_fake_dir_create_times(SNUM(conn))); + create_ts = sbuf.st_ex_btime; srv_put_dos_date2((char *)req->outbuf, smb_vwv0, create_ts.tv_sec); - srv_put_dos_date2((char *)req->outbuf, smb_vwv2, sbuf.st_atime); + srv_put_dos_date2((char *)req->outbuf, smb_vwv2, + convert_timespec_to_time_t(sbuf.st_ex_atime)); /* Should we check pending modtime here ? JRA */ - srv_put_dos_date2((char *)req->outbuf, smb_vwv4, sbuf.st_mtime); + srv_put_dos_date2((char *)req->outbuf, smb_vwv4, + convert_timespec_to_time_t(sbuf.st_ex_mtime)); if (mode & aDIR) { SIVAL(req->outbuf, smb_vwv6, 0); SIVAL(req->outbuf, smb_vwv8, 0); } else { - uint32 allocation_size = get_allocation_size(conn,fsp, &sbuf); - SIVAL(req->outbuf, smb_vwv6, (uint32)sbuf.st_size); + uint32 allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn,fsp, &sbuf); + SIVAL(req->outbuf, smb_vwv6, (uint32)sbuf.st_ex_size); SIVAL(req->outbuf, smb_vwv8, allocation_size); } SSVAL(req->outbuf,smb_vwv10, mode);