| 1 | /*
|
|---|
| 2 | Unix SMB/CIFS implementation.
|
|---|
| 3 | file opening and share modes
|
|---|
| 4 | Copyright (C) Andrew Tridgell 1992-1998
|
|---|
| 5 | Copyright (C) Jeremy Allison 2001-2004
|
|---|
| 6 | Copyright (C) Volker Lendecke 2005
|
|---|
| 7 |
|
|---|
| 8 | This program is free software; you can redistribute it and/or modify
|
|---|
| 9 | it under the terms of the GNU General Public License as published by
|
|---|
| 10 | the Free Software Foundation; either version 3 of the License, or
|
|---|
| 11 | (at your option) any later version.
|
|---|
| 12 |
|
|---|
| 13 | This program is distributed in the hope that it will be useful,
|
|---|
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 16 | GNU General Public License for more details.
|
|---|
| 17 |
|
|---|
| 18 | You should have received a copy of the GNU General Public License
|
|---|
| 19 | along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|---|
| 20 | */
|
|---|
| 21 |
|
|---|
| 22 | #include "includes.h"
|
|---|
| 23 |
|
|---|
| 24 | extern const struct generic_mapping file_generic_mapping;
|
|---|
| 25 | extern struct current_user current_user;
|
|---|
| 26 | extern userdom_struct current_user_info;
|
|---|
| 27 | extern bool global_client_failed_oplock_break;
|
|---|
| 28 |
|
|---|
| 29 | struct deferred_open_record {
|
|---|
| 30 | bool delayed_for_oplocks;
|
|---|
| 31 | struct file_id id;
|
|---|
| 32 | };
|
|---|
| 33 |
|
|---|
| 34 | /****************************************************************************
|
|---|
| 35 | fd support routines - attempt to do a dos_open.
|
|---|
| 36 | ****************************************************************************/
|
|---|
| 37 |
|
|---|
| 38 | static NTSTATUS fd_open(struct connection_struct *conn,
|
|---|
| 39 | const char *fname,
|
|---|
| 40 | files_struct *fsp,
|
|---|
| 41 | int flags,
|
|---|
| 42 | mode_t mode)
|
|---|
| 43 | {
|
|---|
| 44 | NTSTATUS status = NT_STATUS_OK;
|
|---|
| 45 |
|
|---|
| 46 | #ifdef O_NOFOLLOW
|
|---|
| 47 | /*
|
|---|
| 48 | * Never follow symlinks on a POSIX client. The
|
|---|
| 49 | * client should be doing this.
|
|---|
| 50 | */
|
|---|
| 51 |
|
|---|
| 52 | if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
|
|---|
| 53 | flags |= O_NOFOLLOW;
|
|---|
| 54 | }
|
|---|
| 55 | #endif
|
|---|
| 56 |
|
|---|
| 57 | fsp->fh->fd = SMB_VFS_OPEN(conn,fname,fsp,flags,mode);
|
|---|
| 58 | if (fsp->fh->fd == -1) {
|
|---|
| 59 | status = map_nt_error_from_unix(errno);
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
|
|---|
| 63 | fname, flags, (int)mode, fsp->fh->fd,
|
|---|
| 64 | (fsp->fh->fd == -1) ? strerror(errno) : "" ));
|
|---|
| 65 |
|
|---|
| 66 | return status;
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | /****************************************************************************
|
|---|
| 70 | Close the file associated with a fsp.
|
|---|
| 71 | ****************************************************************************/
|
|---|
| 72 |
|
|---|
| 73 | NTSTATUS fd_close(files_struct *fsp)
|
|---|
| 74 | {
|
|---|
| 75 | int ret;
|
|---|
| 76 |
|
|---|
| 77 | if (fsp->fh->fd == -1) {
|
|---|
| 78 | return NT_STATUS_OK; /* What we used to call a stat open. */
|
|---|
| 79 | }
|
|---|
| 80 | if (fsp->fh->ref_count > 1) {
|
|---|
| 81 | return NT_STATUS_OK; /* Shared handle. Only close last reference. */
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | ret = SMB_VFS_CLOSE(fsp);
|
|---|
| 85 | fsp->fh->fd = -1;
|
|---|
| 86 | if (ret == -1) {
|
|---|
| 87 | return map_nt_error_from_unix(errno);
|
|---|
| 88 | }
|
|---|
| 89 | return NT_STATUS_OK;
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | /****************************************************************************
|
|---|
| 93 | Change the ownership of a file to that of the parent directory.
|
|---|
| 94 | Do this by fd if possible.
|
|---|
| 95 | ****************************************************************************/
|
|---|
| 96 |
|
|---|
| 97 | static void change_file_owner_to_parent(connection_struct *conn,
|
|---|
| 98 | const char *inherit_from_dir,
|
|---|
| 99 | files_struct *fsp)
|
|---|
| 100 | {
|
|---|
| 101 | SMB_STRUCT_STAT parent_st;
|
|---|
| 102 | int ret;
|
|---|
| 103 |
|
|---|
| 104 | ret = SMB_VFS_STAT(conn, inherit_from_dir, &parent_st);
|
|---|
| 105 | if (ret == -1) {
|
|---|
| 106 | DEBUG(0,("change_file_owner_to_parent: failed to stat parent "
|
|---|
| 107 | "directory %s. Error was %s\n",
|
|---|
| 108 | inherit_from_dir, strerror(errno) ));
|
|---|
| 109 | return;
|
|---|
| 110 | }
|
|---|
| 111 |
|
|---|
| 112 | become_root();
|
|---|
| 113 | ret = SMB_VFS_FCHOWN(fsp, parent_st.st_uid, (gid_t)-1);
|
|---|
| 114 | unbecome_root();
|
|---|
| 115 | if (ret == -1) {
|
|---|
| 116 | DEBUG(0,("change_file_owner_to_parent: failed to fchown "
|
|---|
| 117 | "file %s to parent directory uid %u. Error "
|
|---|
| 118 | "was %s\n", fsp->fsp_name,
|
|---|
| 119 | (unsigned int)parent_st.st_uid,
|
|---|
| 120 | strerror(errno) ));
|
|---|
| 121 | }
|
|---|
| 122 |
|
|---|
| 123 | DEBUG(10,("change_file_owner_to_parent: changed new file %s to "
|
|---|
| 124 | "parent directory uid %u.\n", fsp->fsp_name,
|
|---|
| 125 | (unsigned int)parent_st.st_uid ));
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | static NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
|
|---|
| 129 | const char *inherit_from_dir,
|
|---|
| 130 | const char *fname,
|
|---|
| 131 | SMB_STRUCT_STAT *psbuf)
|
|---|
| 132 | {
|
|---|
| 133 | char *saved_dir = NULL;
|
|---|
| 134 | SMB_STRUCT_STAT sbuf;
|
|---|
| 135 | SMB_STRUCT_STAT parent_st;
|
|---|
| 136 | TALLOC_CTX *ctx = talloc_tos();
|
|---|
| 137 | NTSTATUS status = NT_STATUS_OK;
|
|---|
| 138 | int ret;
|
|---|
| 139 |
|
|---|
| 140 | ret = SMB_VFS_STAT(conn, inherit_from_dir, &parent_st);
|
|---|
| 141 | if (ret == -1) {
|
|---|
| 142 | status = map_nt_error_from_unix(errno);
|
|---|
| 143 | DEBUG(0,("change_dir_owner_to_parent: failed to stat parent "
|
|---|
| 144 | "directory %s. Error was %s\n",
|
|---|
| 145 | inherit_from_dir, strerror(errno) ));
|
|---|
| 146 | return status;
|
|---|
| 147 | }
|
|---|
| 148 |
|
|---|
| 149 | /* We've already done an lstat into psbuf, and we know it's a
|
|---|
| 150 | directory. If we can cd into the directory and the dev/ino
|
|---|
| 151 | are the same then we can safely chown without races as
|
|---|
| 152 | we're locking the directory in place by being in it. This
|
|---|
| 153 | should work on any UNIX (thanks tridge :-). JRA.
|
|---|
| 154 | */
|
|---|
| 155 |
|
|---|
| 156 | saved_dir = vfs_GetWd(ctx,conn);
|
|---|
| 157 | if (!saved_dir) {
|
|---|
| 158 | status = map_nt_error_from_unix(errno);
|
|---|
| 159 | DEBUG(0,("change_dir_owner_to_parent: failed to get "
|
|---|
| 160 | "current working directory. Error was %s\n",
|
|---|
| 161 | strerror(errno)));
|
|---|
| 162 | return status;
|
|---|
| 163 | }
|
|---|
| 164 |
|
|---|
| 165 | /* Chdir into the new path. */
|
|---|
| 166 | if (vfs_ChDir(conn, fname) == -1) {
|
|---|
| 167 | status = map_nt_error_from_unix(errno);
|
|---|
| 168 | DEBUG(0,("change_dir_owner_to_parent: failed to change "
|
|---|
| 169 | "current working directory to %s. Error "
|
|---|
| 170 | "was %s\n", fname, strerror(errno) ));
|
|---|
| 171 | goto out;
|
|---|
| 172 | }
|
|---|
| 173 |
|
|---|
| 174 | if (SMB_VFS_STAT(conn,".",&sbuf) == -1) {
|
|---|
| 175 | status = map_nt_error_from_unix(errno);
|
|---|
| 176 | DEBUG(0,("change_dir_owner_to_parent: failed to stat "
|
|---|
| 177 | "directory '.' (%s) Error was %s\n",
|
|---|
| 178 | fname, strerror(errno)));
|
|---|
| 179 | goto out;
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | /* Ensure we're pointing at the same place. */
|
|---|
| 183 | if (sbuf.st_dev != psbuf->st_dev ||
|
|---|
| 184 | sbuf.st_ino != psbuf->st_ino ||
|
|---|
| 185 | sbuf.st_mode != psbuf->st_mode ) {
|
|---|
| 186 | DEBUG(0,("change_dir_owner_to_parent: "
|
|---|
| 187 | "device/inode/mode on directory %s changed. "
|
|---|
| 188 | "Refusing to chown !\n", fname ));
|
|---|
| 189 | status = NT_STATUS_ACCESS_DENIED;
|
|---|
| 190 | goto out;
|
|---|
| 191 | }
|
|---|
| 192 |
|
|---|
| 193 | become_root();
|
|---|
| 194 | ret = SMB_VFS_CHOWN(conn, ".", parent_st.st_uid, (gid_t)-1);
|
|---|
| 195 | unbecome_root();
|
|---|
| 196 | if (ret == -1) {
|
|---|
| 197 | status = map_nt_error_from_unix(errno);
|
|---|
| 198 | DEBUG(10,("change_dir_owner_to_parent: failed to chown "
|
|---|
| 199 | "directory %s to parent directory uid %u. "
|
|---|
| 200 | "Error was %s\n", fname,
|
|---|
| 201 | (unsigned int)parent_st.st_uid, strerror(errno) ));
|
|---|
| 202 | goto out;
|
|---|
| 203 | }
|
|---|
| 204 |
|
|---|
| 205 | DEBUG(10,("change_dir_owner_to_parent: changed ownership of new "
|
|---|
| 206 | "directory %s to parent directory uid %u.\n",
|
|---|
| 207 | fname, (unsigned int)parent_st.st_uid ));
|
|---|
| 208 |
|
|---|
| 209 | out:
|
|---|
| 210 |
|
|---|
| 211 | vfs_ChDir(conn,saved_dir);
|
|---|
| 212 | return status;
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | /****************************************************************************
|
|---|
| 216 | Open a file.
|
|---|
| 217 | ****************************************************************************/
|
|---|
| 218 |
|
|---|
| 219 | static NTSTATUS open_file(files_struct *fsp,
|
|---|
| 220 | connection_struct *conn,
|
|---|
| 221 | struct smb_request *req,
|
|---|
| 222 | const char *parent_dir,
|
|---|
| 223 | const char *name,
|
|---|
| 224 | const char *path,
|
|---|
| 225 | SMB_STRUCT_STAT *psbuf,
|
|---|
| 226 | int flags,
|
|---|
| 227 | mode_t unx_mode,
|
|---|
| 228 | uint32 access_mask, /* client requested access mask. */
|
|---|
| 229 | uint32 open_access_mask) /* what we're actually using in the open. */
|
|---|
| 230 | {
|
|---|
| 231 | NTSTATUS status = NT_STATUS_OK;
|
|---|
| 232 | int accmode = (flags & O_ACCMODE);
|
|---|
| 233 | int local_flags = flags;
|
|---|
| 234 | bool file_existed = VALID_STAT(*psbuf);
|
|---|
| 235 |
|
|---|
| 236 | fsp->fh->fd = -1;
|
|---|
| 237 | errno = EPERM;
|
|---|
| 238 |
|
|---|
| 239 | /* Check permissions */
|
|---|
| 240 |
|
|---|
| 241 | /*
|
|---|
| 242 | * This code was changed after seeing a client open request
|
|---|
| 243 | * containing the open mode of (DENY_WRITE/read-only) with
|
|---|
| 244 | * the 'create if not exist' bit set. The previous code
|
|---|
| 245 | * would fail to open the file read only on a read-only share
|
|---|
| 246 | * as it was checking the flags parameter directly against O_RDONLY,
|
|---|
| 247 | * this was failing as the flags parameter was set to O_RDONLY|O_CREAT.
|
|---|
| 248 | * JRA.
|
|---|
| 249 | */
|
|---|
| 250 |
|
|---|
| 251 | if (!CAN_WRITE(conn)) {
|
|---|
| 252 | /* It's a read-only share - fail if we wanted to write. */
|
|---|
| 253 | if(accmode != O_RDONLY) {
|
|---|
| 254 | DEBUG(3,("Permission denied opening %s\n", path));
|
|---|
| 255 | return NT_STATUS_ACCESS_DENIED;
|
|---|
| 256 | } else if(flags & O_CREAT) {
|
|---|
| 257 | /* We don't want to write - but we must make sure that
|
|---|
| 258 | O_CREAT doesn't create the file if we have write
|
|---|
| 259 | access into the directory.
|
|---|
| 260 | */
|
|---|
| 261 | flags &= ~O_CREAT;
|
|---|
| 262 | local_flags &= ~O_CREAT;
|
|---|
| 263 | }
|
|---|
| 264 | }
|
|---|
| 265 |
|
|---|
| 266 | /*
|
|---|
| 267 | * This little piece of insanity is inspired by the
|
|---|
| 268 | * fact that an NT client can open a file for O_RDONLY,
|
|---|
| 269 | * but set the create disposition to FILE_EXISTS_TRUNCATE.
|
|---|
| 270 | * If the client *can* write to the file, then it expects to
|
|---|
| 271 | * truncate the file, even though it is opening for readonly.
|
|---|
| 272 | * Quicken uses this stupid trick in backup file creation...
|
|---|
| 273 | * Thanks *greatly* to "David W. Chapman Jr." <[email protected]>
|
|---|
| 274 | * for helping track this one down. It didn't bite us in 2.0.x
|
|---|
| 275 | * as we always opened files read-write in that release. JRA.
|
|---|
| 276 | */
|
|---|
| 277 |
|
|---|
| 278 | if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
|
|---|
| 279 | DEBUG(10,("open_file: truncate requested on read-only open "
|
|---|
| 280 | "for file %s\n", path));
|
|---|
| 281 | local_flags = (flags & ~O_ACCMODE)|O_RDWR;
|
|---|
| 282 | }
|
|---|
| 283 |
|
|---|
| 284 | if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
|
|---|
| 285 | (!file_existed && (local_flags & O_CREAT)) ||
|
|---|
| 286 | ((local_flags & O_TRUNC) == O_TRUNC) ) {
|
|---|
| 287 | const char *wild;
|
|---|
| 288 |
|
|---|
| 289 | /*
|
|---|
| 290 | * We can't actually truncate here as the file may be locked.
|
|---|
| 291 | * open_file_ntcreate will take care of the truncate later. JRA.
|
|---|
| 292 | */
|
|---|
| 293 |
|
|---|
| 294 | local_flags &= ~O_TRUNC;
|
|---|
| 295 |
|
|---|
| 296 | #if defined(O_NONBLOCK) && defined(S_ISFIFO)
|
|---|
| 297 | /*
|
|---|
| 298 | * We would block on opening a FIFO with no one else on the
|
|---|
| 299 | * other end. Do what we used to do and add O_NONBLOCK to the
|
|---|
| 300 | * open flags. JRA.
|
|---|
| 301 | */
|
|---|
| 302 |
|
|---|
| 303 | if (file_existed && S_ISFIFO(psbuf->st_mode)) {
|
|---|
| 304 | local_flags |= O_NONBLOCK;
|
|---|
| 305 | }
|
|---|
| 306 | #endif
|
|---|
| 307 |
|
|---|
| 308 | /* Don't create files with Microsoft wildcard characters. */
|
|---|
| 309 | if (fsp->base_fsp) {
|
|---|
| 310 | /*
|
|---|
| 311 | * wildcard characters are allowed in stream names
|
|---|
| 312 | * only test the basefilename
|
|---|
| 313 | */
|
|---|
| 314 | wild = fsp->base_fsp->fsp_name;
|
|---|
| 315 | } else {
|
|---|
| 316 | wild = path;
|
|---|
| 317 | }
|
|---|
| 318 | if ((local_flags & O_CREAT) && !file_existed &&
|
|---|
| 319 | ms_has_wild(wild)) {
|
|---|
| 320 | return NT_STATUS_OBJECT_NAME_INVALID;
|
|---|
| 321 | }
|
|---|
| 322 |
|
|---|
| 323 | /* Actually do the open */
|
|---|
| 324 | status = fd_open(conn, path, fsp, local_flags, unx_mode);
|
|---|
| 325 | if (!NT_STATUS_IS_OK(status)) {
|
|---|
| 326 | DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
|
|---|
| 327 | "(flags=%d)\n",
|
|---|
| 328 | path,nt_errstr(status),local_flags,flags));
|
|---|
| 329 | return status;
|
|---|
| 330 | }
|
|---|
| 331 |
|
|---|
| 332 | if ((local_flags & O_CREAT) && !file_existed) {
|
|---|
| 333 |
|
|---|
| 334 | /* Inherit the ACL if required */
|
|---|
| 335 | if (lp_inherit_perms(SNUM(conn))) {
|
|---|
| 336 | inherit_access_acl(conn, parent_dir, path,
|
|---|
| 337 | unx_mode);
|
|---|
| 338 | }
|
|---|
| 339 |
|
|---|
| 340 | /* Change the owner if required. */
|
|---|
| 341 | if (lp_inherit_owner(SNUM(conn))) {
|
|---|
| 342 | change_file_owner_to_parent(conn, parent_dir,
|
|---|
| 343 | fsp);
|
|---|
| 344 | }
|
|---|
| 345 |
|
|---|
| 346 | notify_fname(conn, NOTIFY_ACTION_ADDED,
|
|---|
| 347 | FILE_NOTIFY_CHANGE_FILE_NAME, path);
|
|---|
| 348 | }
|
|---|
| 349 |
|
|---|
| 350 | } else {
|
|---|
| 351 | fsp->fh->fd = -1; /* What we used to call a stat open. */
|
|---|
| 352 | }
|
|---|
| 353 |
|
|---|
| 354 | if (!file_existed) {
|
|---|
| 355 | int ret;
|
|---|
| 356 |
|
|---|
| 357 | if (fsp->fh->fd == -1) {
|
|---|
| 358 | ret = SMB_VFS_STAT(conn, path, psbuf);
|
|---|
| 359 | } else {
|
|---|
| 360 | ret = SMB_VFS_FSTAT(fsp, psbuf);
|
|---|
| 361 | /* If we have an fd, this stat should succeed. */
|
|---|
| 362 | if (ret == -1) {
|
|---|
| 363 | DEBUG(0,("Error doing fstat on open file %s "
|
|---|
| 364 | "(%s)\n", path,strerror(errno) ));
|
|---|
| 365 | }
|
|---|
| 366 | }
|
|---|
| 367 |
|
|---|
| 368 | /* For a non-io open, this stat failing means file not found. JRA */
|
|---|
| 369 | if (ret == -1) {
|
|---|
| 370 | status = map_nt_error_from_unix(errno);
|
|---|
| 371 | fd_close(fsp);
|
|---|
| 372 | return status;
|
|---|
| 373 | }
|
|---|
| 374 | }
|
|---|
| 375 |
|
|---|
| 376 | /*
|
|---|
| 377 | * POSIX allows read-only opens of directories. We don't
|
|---|
| 378 | * want to do this (we use a different code path for this)
|
|---|
| 379 | * so catch a directory open and return an EISDIR. JRA.
|
|---|
| 380 | */
|
|---|
| 381 |
|
|---|
| 382 | if(S_ISDIR(psbuf->st_mode)) {
|
|---|
| 383 | fd_close(fsp);
|
|---|
| 384 | errno = EISDIR;
|
|---|
| 385 | return NT_STATUS_FILE_IS_A_DIRECTORY;
|
|---|
| 386 | }
|
|---|
| 387 |
|
|---|
| 388 | fsp->mode = psbuf->st_mode;
|
|---|
| 389 | fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
|
|---|
| 390 | fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
|
|---|
| 391 | fsp->file_pid = req ? req->smbpid : 0;
|
|---|
| 392 | fsp->can_lock = True;
|
|---|
| 393 | fsp->can_read = (access_mask & (FILE_READ_DATA)) ? True : False;
|
|---|
| 394 | if (!CAN_WRITE(conn)) {
|
|---|
| 395 | fsp->can_write = False;
|
|---|
| 396 | } else {
|
|---|
| 397 | fsp->can_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ?
|
|---|
| 398 | True : False;
|
|---|
| 399 | }
|
|---|
| 400 | fsp->print_file = False;
|
|---|
| 401 | fsp->modified = False;
|
|---|
| 402 | fsp->sent_oplock_break = NO_BREAK_SENT;
|
|---|
| 403 | fsp->is_directory = False;
|
|---|
| 404 | fsp->is_stat = False;
|
|---|
| 405 | if (conn->aio_write_behind_list &&
|
|---|
| 406 | is_in_path(path, conn->aio_write_behind_list, conn->case_sensitive)) {
|
|---|
| 407 | fsp->aio_write_behind = True;
|
|---|
| 408 | }
|
|---|
| 409 |
|
|---|
| 410 | string_set(&fsp->fsp_name, path);
|
|---|
| 411 | fsp->wcp = NULL; /* Write cache pointer. */
|
|---|
| 412 |
|
|---|
| 413 | DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
|
|---|
| 414 | *current_user_info.smb_name ?
|
|---|
| 415 | current_user_info.smb_name : conn->user,fsp->fsp_name,
|
|---|
| 416 | BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
|
|---|
| 417 | conn->num_files_open));
|
|---|
| 418 |
|
|---|
| 419 | errno = 0;
|
|---|
| 420 | return NT_STATUS_OK;
|
|---|
| 421 | }
|
|---|
| 422 |
|
|---|
| 423 | /*******************************************************************
|
|---|
| 424 | Return True if the filename is one of the special executable types.
|
|---|
| 425 | ********************************************************************/
|
|---|
| 426 |
|
|---|
| 427 | static bool is_executable(const char *fname)
|
|---|
| 428 | {
|
|---|
| 429 | if ((fname = strrchr_m(fname,'.'))) {
|
|---|
| 430 | if (strequal(fname,".com") ||
|
|---|
| 431 | strequal(fname,".dll") ||
|
|---|
| 432 | strequal(fname,".exe") ||
|
|---|
| 433 | strequal(fname,".sym")) {
|
|---|
| 434 | return True;
|
|---|
| 435 | }
|
|---|
| 436 | }
|
|---|
| 437 | return False;
|
|---|
| 438 | }
|
|---|
| 439 |
|
|---|
| 440 | /****************************************************************************
|
|---|
| 441 | Check if we can open a file with a share mode.
|
|---|
| 442 | Returns True if conflict, False if not.
|
|---|
| 443 | ****************************************************************************/
|
|---|
| 444 |
|
|---|
| 445 | static bool share_conflict(struct share_mode_entry *entry,
|
|---|
| 446 | uint32 access_mask,
|
|---|
| 447 | uint32 share_access)
|
|---|
| 448 | {
|
|---|
| 449 | DEBUG(10,("share_conflict: entry->access_mask = 0x%x, "
|
|---|
| 450 | "entry->share_access = 0x%x, "
|
|---|
| 451 | "entry->private_options = 0x%x\n",
|
|---|
| 452 | (unsigned int)entry->access_mask,
|
|---|
| 453 | (unsigned int)entry->share_access,
|
|---|
| 454 | (unsigned int)entry->private_options));
|
|---|
| 455 |
|
|---|
| 456 | DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n",
|
|---|
| 457 | (unsigned int)access_mask, (unsigned int)share_access));
|
|---|
| 458 |
|
|---|
| 459 | if ((entry->access_mask & (FILE_WRITE_DATA|
|
|---|
| 460 | FILE_APPEND_DATA|
|
|---|
| 461 | FILE_READ_DATA|
|
|---|
| 462 | FILE_EXECUTE|
|
|---|
| 463 | DELETE_ACCESS)) == 0) {
|
|---|
| 464 | DEBUG(10,("share_conflict: No conflict due to "
|
|---|
| 465 | "entry->access_mask = 0x%x\n",
|
|---|
| 466 | (unsigned int)entry->access_mask ));
|
|---|
| 467 | return False;
|
|---|
| 468 | }
|
|---|
| 469 |
|
|---|
| 470 | if ((access_mask & (FILE_WRITE_DATA|
|
|---|
| 471 | FILE_APPEND_DATA|
|
|---|
| 472 | FILE_READ_DATA|
|
|---|
| 473 | FILE_EXECUTE|
|
|---|
| 474 | DELETE_ACCESS)) == 0) {
|
|---|
| 475 | DEBUG(10,("share_conflict: No conflict due to "
|
|---|
| 476 | "access_mask = 0x%x\n",
|
|---|
| 477 | (unsigned int)access_mask ));
|
|---|
| 478 | return False;
|
|---|
| 479 | }
|
|---|
| 480 |
|
|---|
| 481 | #if 1 /* JRA TEST - Superdebug. */
|
|---|
| 482 | #define CHECK_MASK(num, am, right, sa, share) \
|
|---|
| 483 | DEBUG(10,("share_conflict: [%d] am (0x%x) & right (0x%x) = 0x%x\n", \
|
|---|
| 484 | (unsigned int)(num), (unsigned int)(am), \
|
|---|
| 485 | (unsigned int)(right), (unsigned int)(am)&(right) )); \
|
|---|
| 486 | DEBUG(10,("share_conflict: [%d] sa (0x%x) & share (0x%x) = 0x%x\n", \
|
|---|
| 487 | (unsigned int)(num), (unsigned int)(sa), \
|
|---|
| 488 | (unsigned int)(share), (unsigned int)(sa)&(share) )); \
|
|---|
| 489 | if (((am) & (right)) && !((sa) & (share))) { \
|
|---|
| 490 | DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
|
|---|
| 491 | sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
|
|---|
| 492 | (unsigned int)(share) )); \
|
|---|
| 493 | return True; \
|
|---|
| 494 | }
|
|---|
| 495 | #else
|
|---|
| 496 | #define CHECK_MASK(num, am, right, sa, share) \
|
|---|
| 497 | if (((am) & (right)) && !((sa) & (share))) { \
|
|---|
| 498 | DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
|
|---|
| 499 | sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
|
|---|
| 500 | (unsigned int)(share) )); \
|
|---|
| 501 | return True; \
|
|---|
| 502 | }
|
|---|
| 503 | #endif
|
|---|
| 504 |
|
|---|
| 505 | CHECK_MASK(1, entry->access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
|
|---|
| 506 | share_access, FILE_SHARE_WRITE);
|
|---|
| 507 | CHECK_MASK(2, access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
|
|---|
| 508 | entry->share_access, FILE_SHARE_WRITE);
|
|---|
| 509 |
|
|---|
| 510 | CHECK_MASK(3, entry->access_mask, FILE_READ_DATA | FILE_EXECUTE,
|
|---|
| 511 | share_access, FILE_SHARE_READ);
|
|---|
| 512 | CHECK_MASK(4, access_mask, FILE_READ_DATA | FILE_EXECUTE,
|
|---|
| 513 | entry->share_access, FILE_SHARE_READ);
|
|---|
| 514 |
|
|---|
| 515 | CHECK_MASK(5, entry->access_mask, DELETE_ACCESS,
|
|---|
| 516 | share_access, FILE_SHARE_DELETE);
|
|---|
| 517 | CHECK_MASK(6, access_mask, DELETE_ACCESS,
|
|---|
| 518 | entry->share_access, FILE_SHARE_DELETE);
|
|---|
| 519 |
|
|---|
| 520 | DEBUG(10,("share_conflict: No conflict.\n"));
|
|---|
| 521 | return False;
|
|---|
| 522 | }
|
|---|
| 523 |
|
|---|
| 524 | #if defined(DEVELOPER)
|
|---|
| 525 | static void validate_my_share_entries(int num,
|
|---|
| 526 | struct share_mode_entry *share_entry)
|
|---|
| 527 | {
|
|---|
| 528 | files_struct *fsp;
|
|---|
| 529 |
|
|---|
| 530 | if (!procid_is_me(&share_entry->pid)) {
|
|---|
| 531 | return;
|
|---|
| 532 | }
|
|---|
| 533 |
|
|---|
| 534 | if (is_deferred_open_entry(share_entry) &&
|
|---|
| 535 | !open_was_deferred(share_entry->op_mid)) {
|
|---|
| 536 | char *str = talloc_asprintf(talloc_tos(),
|
|---|
| 537 | "Got a deferred entry without a request: "
|
|---|
| 538 | "PANIC: %s\n",
|
|---|
| 539 | share_mode_str(talloc_tos(), num, share_entry));
|
|---|
| 540 | smb_panic(str);
|
|---|
| 541 | }
|
|---|
| 542 |
|
|---|
| 543 | if (!is_valid_share_mode_entry(share_entry)) {
|
|---|
| 544 | return;
|
|---|
| 545 | }
|
|---|
| 546 |
|
|---|
| 547 | fsp = file_find_dif(share_entry->id,
|
|---|
| 548 | share_entry->share_file_id);
|
|---|
| 549 | if (!fsp) {
|
|---|
| 550 | DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
|
|---|
| 551 | share_mode_str(talloc_tos(), num, share_entry) ));
|
|---|
| 552 | smb_panic("validate_my_share_entries: Cannot match a "
|
|---|
| 553 | "share entry with an open file\n");
|
|---|
| 554 | }
|
|---|
| 555 |
|
|---|
| 556 | if (is_deferred_open_entry(share_entry) ||
|
|---|
| 557 | is_unused_share_mode_entry(share_entry)) {
|
|---|
| 558 | goto panic;
|
|---|
| 559 | }
|
|---|
| 560 |
|
|---|
| 561 | if ((share_entry->op_type == NO_OPLOCK) &&
|
|---|
| 562 | (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) {
|
|---|
| 563 | /* Someone has already written to it, but I haven't yet
|
|---|
| 564 | * noticed */
|
|---|
| 565 | return;
|
|---|
| 566 | }
|
|---|
| 567 |
|
|---|
| 568 | if (((uint16)fsp->oplock_type) != share_entry->op_type) {
|
|---|
| 569 | goto panic;
|
|---|
| 570 | }
|
|---|
| 571 |
|
|---|
| 572 | return;
|
|---|
| 573 |
|
|---|
| 574 | panic:
|
|---|
| 575 | {
|
|---|
| 576 | char *str;
|
|---|
| 577 | DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
|
|---|
| 578 | share_mode_str(talloc_tos(), num, share_entry) ));
|
|---|
| 579 | str = talloc_asprintf(talloc_tos(),
|
|---|
| 580 | "validate_my_share_entries: "
|
|---|
| 581 | "file %s, oplock_type = 0x%x, op_type = 0x%x\n",
|
|---|
| 582 | fsp->fsp_name, (unsigned int)fsp->oplock_type,
|
|---|
| 583 | (unsigned int)share_entry->op_type );
|
|---|
| 584 | smb_panic(str);
|
|---|
| 585 | }
|
|---|
| 586 | }
|
|---|
| 587 | #endif
|
|---|
| 588 |
|
|---|
| 589 | static bool is_stat_open(uint32 access_mask)
|
|---|
| 590 | {
|
|---|
| 591 | return (access_mask &&
|
|---|
| 592 | ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES|
|
|---|
| 593 | FILE_WRITE_ATTRIBUTES))==0) &&
|
|---|
| 594 | ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|
|
|---|
| 595 | FILE_WRITE_ATTRIBUTES)) != 0));
|
|---|
| 596 | }
|
|---|
| 597 |
|
|---|
| 598 | /****************************************************************************
|
|---|
| 599 | Deal with share modes
|
|---|
| 600 | Invarient: Share mode must be locked on entry and exit.
|
|---|
| 601 | Returns -1 on error, or number of share modes on success (may be zero).
|
|---|
| 602 | ****************************************************************************/
|
|---|
| 603 |
|
|---|
| 604 | static NTSTATUS open_mode_check(connection_struct *conn,
|
|---|
| 605 | const char *fname,
|
|---|
| 606 | struct share_mode_lock *lck,
|
|---|
| 607 | uint32 access_mask,
|
|---|
| 608 | uint32 share_access,
|
|---|
| 609 | uint32 create_options,
|
|---|
| 610 | bool *file_existed)
|
|---|
| 611 | {
|
|---|
| 612 | int i;
|
|---|
| 613 |
|
|---|
| 614 | if(lck->num_share_modes == 0) {
|
|---|
| 615 | return NT_STATUS_OK;
|
|---|
| 616 | }
|
|---|
| 617 |
|
|---|
| 618 | *file_existed = True;
|
|---|
| 619 |
|
|---|
| 620 | /* A delete on close prohibits everything */
|
|---|
| 621 |
|
|---|
| 622 | if (lck->delete_on_close) {
|
|---|
| 623 | return NT_STATUS_DELETE_PENDING;
|
|---|
| 624 | }
|
|---|
| 625 |
|
|---|
| 626 | if (is_stat_open(access_mask)) {
|
|---|
| 627 | /* Stat open that doesn't trigger oplock breaks or share mode
|
|---|
| 628 | * checks... ! JRA. */
|
|---|
| 629 | return NT_STATUS_OK;
|
|---|
| 630 | }
|
|---|
| 631 |
|
|---|
| 632 | /*
|
|---|
| 633 | * Check if the share modes will give us access.
|
|---|
| 634 | */
|
|---|
| 635 |
|
|---|
| 636 | #if defined(DEVELOPER)
|
|---|
|
|---|