📄 pvfs_open.c
字号:
return; } /* re-mark it async, just in case someone up the chain does paranoid checking */ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; /* send the reply up the chain */ req->async_states->status = status; req->async_states->send_fn(req);}/* special handling for openx DENY_DOS semantics This function attempts a reference open using an existing handle. If its allowed, then it returns NT_STATUS_OK, otherwise it returns any other code and normal open processing continues.*/static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_open *io, struct pvfs_file *f, struct odb_lock *lck){ struct pvfs_state *pvfs = ntvfs->private_data; struct pvfs_file *f2; struct pvfs_filename *name; NTSTATUS status; /* search for an existing open with the right parameters. Note the magic ntcreatex options flag, which is set in the generic mapping code. This might look ugly, but its actually pretty much now w2k does it internally as well. If you look at the BASE-DENYDOS test you will see that a DENY_DOS is a very special case, and in the right circumstances you actually get the _same_ handle back twice, rather than a new handle. */ for (f2=pvfs->files.list;f2;f2=f2->next) { if (f2 != f && f2->ntvfs->session_info == req->session_info && f2->ntvfs->smbpid == req->smbpid && (f2->handle->create_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) && (f2->access_mask & SEC_FILE_WRITE_DATA) && strcasecmp_m(f2->handle->name->original_name, io->generic.in.fname)==0) { break; } } if (!f2) { return NT_STATUS_SHARING_VIOLATION; } /* quite an insane set of semantics ... */ if (is_exe_filename(io->generic.in.fname) && (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) { return NT_STATUS_SHARING_VIOLATION; } /* setup a reference to the existing handle */ talloc_free(f->handle); f->handle = talloc_reference(f, f2->handle); talloc_free(lck); name = f->handle->name; io->generic.out.oplock_level = OPLOCK_NONE; io->generic.out.file.ntvfs = f->ntvfs; io->generic.out.create_action = NTCREATEX_ACTION_EXISTED; io->generic.out.create_time = name->dos.create_time; io->generic.out.access_time = name->dos.access_time; io->generic.out.write_time = name->dos.write_time; io->generic.out.change_time = name->dos.change_time; io->generic.out.attrib = name->dos.attrib; io->generic.out.alloc_size = name->dos.alloc_size; io->generic.out.size = name->st.st_size; io->generic.out.file_type = FILE_TYPE_DISK; io->generic.out.ipc_state = 0; io->generic.out.is_directory = 0; status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f); NT_STATUS_NOT_OK_RETURN(status); return NT_STATUS_OK;}/* setup for a open retry after a sharing violation*/static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_open *io, struct pvfs_file *f, struct odb_lock *lck, NTSTATUS parent_status){ struct pvfs_state *pvfs = ntvfs->private_data; NTSTATUS status; struct timeval end_time; struct timeval *final_timeout = NULL; if (io->generic.in.create_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { /* see if we can satisfy the request using the special DENY_DOS code */ status = pvfs_open_deny_dos(ntvfs, req, io, f, lck); if (NT_STATUS_IS_OK(status)) { return status; } } /* the retry should allocate a new file handle */ talloc_free(f); if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) { end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay); } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) { end_time = timeval_add(&req->statistics.request_time, pvfs->oplock_break_timeout, 0); } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) { /* * we got EAGAIN which means a unix application * has an oplock or share mode * * we retry every 4/5 of the sharing violation delay * to see if the unix application * has released the oplock or share mode. */ final_timeout = talloc(req, struct timeval); NT_STATUS_HAVE_NO_MEMORY(final_timeout); *final_timeout = timeval_add(&req->statistics.request_time, pvfs->oplock_break_timeout, 0); end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5); end_time = timeval_min(final_timeout, &end_time); } else { return NT_STATUS_INTERNAL_ERROR; } return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, final_timeout, pvfs_retry_open_sharing);}/* open a file*/NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, union smb_open *io){ struct pvfs_state *pvfs = ntvfs->private_data; int flags; struct pvfs_filename *name; struct pvfs_file *f; struct ntvfs_handle *h; NTSTATUS status; int fd; struct odb_lock *lck; uint32_t create_options; uint32_t share_access; uint32_t access_mask; uint32_t create_action = NTCREATEX_ACTION_EXISTED; bool del_on_close; bool stream_existed, stream_truncate=false; uint32_t oplock_level = OPLOCK_NONE, oplock_granted; bool allow_level_II_oplock = false; /* use the generic mapping code to avoid implementing all the different open calls. */ if (io->generic.level != RAW_OPEN_GENERIC && io->generic.level != RAW_OPEN_NTTRANS_CREATE) { return ntvfs_map_open(ntvfs, req, io); } ZERO_STRUCT(io->generic.out); create_options = io->generic.in.create_options; share_access = io->generic.in.share_access; access_mask = io->generic.in.access_mask; if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) { return NT_STATUS_INVALID_PARAMETER; } /* some create options are not supported */ if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) { return NT_STATUS_NOT_SUPPORTED; } /* other create options are not allowed */ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && !(access_mask & SEC_STD_DELETE)) { return NT_STATUS_INVALID_PARAMETER; } if (access_mask & SEC_MASK_INVALID) { return NT_STATUS_ACCESS_DENIED; } /* what does this bit really mean?? */ if (req->ctx->protocol == PROTOCOL_SMB2 && access_mask == SEC_STD_SYNCHRONIZE) { return NT_STATUS_ACCESS_DENIED; } if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE| FILE_ATTRIBUTE_VOLUME| (~FILE_ATTRIBUTE_ALL_MASK))) { return NT_STATUS_INVALID_PARAMETER; } /* we ignore some file_attr bits */ io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_SPARSE | FILE_ATTRIBUTE_NORMAL); /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, PVFS_RESOLVE_STREAMS, &name); if (!NT_STATUS_IS_OK(status)) { return status; } /* if the client specified that it must not be a directory then check that it isn't */ if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) { return NT_STATUS_FILE_IS_A_DIRECTORY; } /* if the client specified that it must be a directory then check that it is */ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) { return NT_STATUS_NOT_A_DIRECTORY; } /* directory opens are handled separately */ if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) || (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) { return pvfs_open_directory(pvfs, req, name, io); } /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory open doesn't match */ io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY; flags = 0; switch (io->generic.in.open_disposition) { case NTCREATEX_DISP_SUPERSEDE: case NTCREATEX_DISP_OVERWRITE_IF: if (name->stream_name == NULL) { flags = O_TRUNC; } else { stream_truncate = true; } create_action = NTCREATEX_ACTION_TRUNCATED; break; case NTCREATEX_DISP_OPEN: if (!name->stream_exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } flags = 0; break; case NTCREATEX_DISP_OVERWRITE: if (!name->stream_exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (name->stream_name == NULL) { flags = O_TRUNC; } else { stream_truncate = true; } create_action = NTCREATEX_ACTION_TRUNCATED; break; case NTCREATEX_DISP_CREATE: if (name->stream_exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } flags = 0; break; case NTCREATEX_DISP_OPEN_IF: flags = 0; break; default: return NT_STATUS_INVALID_PARAMETER; } /* handle creating a new file separately */ if (!name->exists) { status = pvfs_create_file(pvfs, req, name, io); if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { return status; } /* we've hit a race - the file was created during this call */ if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) { return status; } /* try re-resolving the name */ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name); if (!NT_STATUS_IS_OK(status)) { return status; } /* fall through to a normal open */ } if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) && (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) { return NT_STATUS_CANNOT_DELETE; } /* check the security descriptor */ status = pvfs_access_check(pvfs, req, name, &access_mask); NT_STATUS_NOT_OK_RETURN(status); if (io->generic.in.query_maximal_access) { status = pvfs_access_maximal_allowed(pvfs, req, name, &io->generic.out.maximal_access); NT_STATUS_NOT_OK_RETURN(status); } status = ntvfs_handle_new(pvfs->ntvfs, req, &h); NT_STATUS_NOT_OK_RETURN(status); f = talloc(h, struct pvfs_file); if (f == NULL) { return NT_STATUS_NO_MEMORY; } f->handle = talloc(f, struct pvfs_file_handle); if (f->handle == NULL) { return NT_STATUS_NO_MEMORY; } f->ntvfs = h; f->pvfs = pvfs; f->pending_list = NULL; f->lock_count = 0; f->share_access = io->generic.in.share_access; f->access_mask = access_mask; f->impersonation = io->generic.in.impersonation; f->notify_buffer = NULL; f->search = NULL; f->handle->pvfs = pvfs; f->handle->fd = -1; f->handle->name = talloc_steal(f->handle, name); f->handle->create_options = io->generic.in.create_options; f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; f->handle->oplock = NULL; f->handle->have_opendb_entry = false; f->handle->open_completed = false; /* form the lock context used for byte range locking and opendb locking */ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key); if (!NT_STATUS_IS_OK(status)) { return status; } status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle); if (!NT_STATUS_IS_OK(status)) { return status; } /* get a lock on this file before the actual open */ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key); if (lck == NULL) { DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n", name->full_name)); /* we were supposed to do a blocking lock, so something is badly wrong! */ return NT_STATUS_INTERNAL_DB_CORRUPTION; } DLIST_ADD(pvfs->files.list, f); /* setup a destructor to avoid file descriptor leaks on abnormal termination */ talloc_set_destructor(f, pvfs_fnum_destructor); talloc_set_destructor(f->handle, pvfs_handle_destructor); /* * Only SMB2 takes care of the delete_on_close, * on existing files */ if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE && req->ctx->protocol == PROTOCOL_SMB2) { del_on_close = true; } else { del_on_close = false; } if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) { oplock_level = OPLOCK_NONE; } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) { oplock_level = OPLOCK_BATCH; } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) { oplock_level = OPLOCK_EXCLUSIVE; } if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) { allow_level_II_oplock = true; } /* see if we are allowed to open at the same time as existing opens */ status = odb_can_open(lck, name->stream_id, share_access, access_mask, del_on_close, io->generic.in.open_disposition, false); /* * on a sharing violation we need to retry when the file is closed by * the other user, or after 1 second * on a non granted oplock we need to retry when the file is closed by * the other user, or after 30 seconds */ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) || NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) && (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status); } if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); return status; } if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) { flags |= O_RDWR; } else { flags |= O_RDONLY; } /* do the actual open */ fd = open(f->handle->name->full_name, flags | O_NONBLOCK); if (fd == -1) { status = pvfs_map_errno(f->pvfs, errno); /* * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK */ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status); } talloc_free(lck); return status; } f->handle->fd = fd; /* now really mark the file as open */ status = odb_open_file(lck, f->handle, name->full_name, &f->handle->fd, allow_level_II_oplock, oplock_level, &oplock_granted); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); return status; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -