📄 posix.c
字号:
DEBUG(10,("truncate low case: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size )); l_curr = l_curr->next; } else if ( (l_curr->start < lock->start) && (l_curr->start + l_curr->size > lock->start + lock->size) ) { /* * Worst case scenario. Unlock request completely overlaps an existing * lock range. Split the request into two, push the new (upper) request * into the dlink list, and continue with the entry after ul_new (as we * know that ul_new will not overlap with this lock). *//********************************************* +---------------------------+ | l_curr | +---------------------------+ +---------+ | lock | +---------+BECOMES..... +-------+ +---------+ | l_curr| | l_new | +-------+ +---------+**********************************************/ struct lock_list *l_new = TALLOC_P(ctx, struct lock_list); if(l_new == NULL) { DEBUG(0,("posix_lock_list: talloc fail.\n")); return NULL; /* The talloc_destroy takes care of cleanup. */ } ZERO_STRUCTP(l_new); l_new->start = lock->start + lock->size; l_new->size = l_curr->start + l_curr->size - l_new->start; /* Truncate the l_curr. */ l_curr->size = lock->start - l_curr->start; DEBUG(10,("split case: curr: start=%.0f,size=%.0f \new: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size, (double)l_new->start, (double)l_new->size )); /* * Add into the dlink list after the l_curr point - NOT at lhead. * Note we can't use DLINK_ADD here as this inserts at the head of the given list. */ l_new->prev = l_curr; l_new->next = l_curr->next; l_curr->next = l_new; /* And move after the link we added. */ l_curr = l_new->next; } else { /* * This logic case should never happen. Ensure this is the * case by forcing an abort.... Remove in production. */ pstring msg; slprintf(msg, sizeof(msg)-1, "logic flaw in cases: l_curr: start = %.0f, size = %.0f : \lock: start = %.0f, size = %.0f\n", (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size ); smb_panic(msg); } } /* end for ( l_curr = lhead; l_curr;) */ } /* end for (i=0; i<num_locks && ul_head; i++) */ SAFE_FREE(dbuf.dptr); return lhead;}/**************************************************************************** POSIX function to acquire a lock. Returns True if the lock could be granted, False if not.****************************************************************************/BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type){ SMB_OFF_T offset; SMB_OFF_T count; BOOL ret = True; size_t entry_num = 0; size_t lock_count; TALLOC_CTX *l_ctx = NULL; struct lock_list *llist = NULL; struct lock_list *ll = NULL; int posix_lock_type = map_posix_lock_type(fsp,lock_type); DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n", fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) )); /* * If the requested lock won't fit in the POSIX range, we will * pretend it was successful. */ if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) return True; /* * Windows is very strange. It allows read locks to be overlayed * (even over a write lock), but leaves the write lock in force until the first * unlock. It also reference counts the locks. This means the following sequence : * * process1 process2 * ------------------------------------------------------------------------ * WRITE LOCK : start = 2, len = 10 * READ LOCK: start =0, len = 10 - FAIL * READ LOCK : start = 0, len = 14 * READ LOCK: start =0, len = 10 - FAIL * UNLOCK : start = 2, len = 10 * READ LOCK: start =0, len = 10 - OK * * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but * would leave a single read lock over the 0-14 region. In order to * re-create Windows semantics mapped to POSIX locks, we create multiple TDB * entries, one for each overlayed lock request. We are guarenteed by the brlock * semantics that if a write lock is added, then it will be first in the array. */ if ((l_ctx = talloc_init("set_posix_lock")) == NULL) { DEBUG(0,("set_posix_lock: unable to init talloc context.\n")); return True; /* Not a fatal error. */ } if ((ll = TALLOC_P(l_ctx, struct lock_list)) == NULL) { DEBUG(0,("set_posix_lock: unable to talloc unlock list.\n")); talloc_destroy(l_ctx); return True; /* Not a fatal error. */ } /* * Create the initial list entry containing the * lock we want to add. */ ZERO_STRUCTP(ll); ll->start = offset; ll->size = count; DLIST_ADD(llist, ll); /* * The following call calculates if there are any * overlapping locks held by this process on * fd's open on the same file and splits this list * into a list of lock ranges that do not overlap with existing * POSIX locks. */ llist = posix_lock_list(l_ctx, llist, fsp); /* * Now we have the list of ranges to lock it is safe to add the * entry into the POSIX lock tdb. We take note of the entry we * added here in case we have to remove it on POSIX lock fail. */ if (!add_posix_lock_entry(fsp,offset,count,posix_lock_type,&entry_num)) { DEBUG(0,("set_posix_lock: Unable to create posix lock entry !\n")); talloc_destroy(l_ctx); return False; } /* * Add the POSIX locks on the list of ranges returned. * As the lock is supposed to be added atomically, we need to * back out all the locks if any one of these calls fail. */ for (lock_count = 0, ll = llist; ll; ll = ll->next, lock_count++) { offset = ll->start; count = ll->size; DEBUG(5,("set_posix_lock: Real lock: Type = %s: offset = %.0f, count = %.0f\n", posix_lock_type_name(posix_lock_type), (double)offset, (double)count )); if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) { DEBUG(5,("set_posix_lock: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n", posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) )); ret = False; break; } } if (!ret) { /* * Back out all the POSIX locks we have on fail. */ for (ll = llist; lock_count; ll = ll->next, lock_count--) { offset = ll->start; count = ll->size; DEBUG(5,("set_posix_lock: Backing out locks: Type = %s: offset = %.0f, count = %.0f\n", posix_lock_type_name(posix_lock_type), (double)offset, (double)count )); posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK); } /* * Remove the tdb entry for this lock. */ delete_posix_lock_entry_by_index(fsp,entry_num); } talloc_destroy(l_ctx); return ret;}/**************************************************************************** POSIX function to release a lock. Returns True if the lock could be released, False if not.****************************************************************************/BOOL release_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count){ SMB_OFF_T offset; SMB_OFF_T count; BOOL ret = True; TALLOC_CTX *ul_ctx = NULL; struct lock_list *ulist = NULL; struct lock_list *ul = NULL; struct posix_lock deleted_lock; int num_overlapped_entries; DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n", fsp->fsp_name, (double)u_offset, (double)u_count )); /* * If the requested lock won't fit in the POSIX range, we will * pretend it was successful. */ if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) return True; /* * We treat this as one unlock request for POSIX accounting purposes even * if it may later be split into multiple smaller POSIX unlock ranges. * num_overlapped_entries is the number of existing locks that have any * overlap with this unlock request. */ num_overlapped_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock); if (num_overlapped_entries == -1) { smb_panic("release_posix_lock: unable find entry to delete !\n"); } /* * If num_overlapped_entries is > 0, and the lock_type we just deleted from the tdb was * a POSIX write lock, then before doing the unlock we need to downgrade * the POSIX lock to a read lock. This allows any overlapping read locks * to be atomically maintained. */ if (num_overlapped_entries > 0 && deleted_lock.lock_type == F_WRLCK) { if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) { DEBUG(0,("release_posix_lock: downgrade of lock failed with error %s !\n", strerror(errno) )); return False; } } if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) { DEBUG(0,("release_posix_lock: unable to init talloc context.\n")); return True; /* Not a fatal error. */ } if ((ul = TALLOC_P(ul_ctx, struct lock_list)) == NULL) { DEBUG(0,("release_posix_lock: unable to talloc unlock list.\n")); talloc_destroy(ul_ctx); return True; /* Not a fatal error. */ } /* * Create the initial list entry containing the * lock we want to remove. */ ZERO_STRUCTP(ul); ul->start = offset; ul->size = count; DLIST_ADD(ulist, ul); /* * The following call calculates if there are any * overlapping locks held by this process on * fd's open on the same file and creates a * list of unlock ranges that will allow * POSIX lock ranges to remain on the file whilst the * unlocks are performed. */ ulist = posix_lock_list(ul_ctx, ulist, fsp); /* * Release the POSIX locks on the list of ranges returned. */ for(; ulist; ulist = ulist->next) { offset = ulist->start; count = ulist->size; DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n", (double)offset, (double)count )); if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) ret = False; } talloc_destroy(ul_ctx); return ret;}/**************************************************************************** Remove all lock entries for a specific dev/inode pair from the tdb.****************************************************************************/static void delete_posix_lock_entries(files_struct *fsp){ TDB_DATA kbuf = locking_key_fsp(fsp); if (tdb_delete(posix_lock_tdb, kbuf) == -1) DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));}/**************************************************************************** Debug function.****************************************************************************/static void dump_entry(struct posix_lock *pl){ DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n", (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));}/**************************************************************************** Remove any locks on this fd. Called from file_close().****************************************************************************/void posix_locking_close_file(files_struct *fsp){ struct posix_lock *entries = NULL; size_t count, i; /* * Optimization for the common case where we are the only * opener of a file. If all fd entries are our own, we don't * need to explicitly release all the locks via the POSIX functions, * we can just remove all the entries in the tdb and allow the * close to remove the real locks. */ count = get_posix_lock_entries(fsp, &entries); if (count == 0) { DEBUG(10,("posix_locking_close_file: file %s has no outstanding locks.\n", fsp->fsp_name )); return; } for (i = 0; i < count; i++) { if (entries[i].fd != fsp->fh->fd ) break; dump_entry(&entries[i]); } if (i == count) { /* All locks are ours. */ DEBUG(10,("posix_locking_close_file: file %s has %u outstanding locks, but all on one fd.\n", fsp->fsp_name, (unsigned int)count )); SAFE_FREE(entries); delete_posix_lock_entries(fsp); return; } /* * Difficult case. We need to delete all our locks, whilst leaving * all other POSIX locks in place. */ for (i = 0; i < count; i++) { struct posix_lock *pl = &entries[i]; if (pl->fd == fsp->fh->fd) release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size ); } SAFE_FREE(entries);}/******************************************************************* Create the in-memory POSIX lock databases.********************************************************************/BOOL posix_locking_init(int read_only){ if (posix_lock_tdb && posix_pending_close_tdb) return True; if (!posix_lock_tdb) posix_lock_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644); if (!posix_lock_tdb) { DEBUG(0,("Failed to open POSIX byte range locking database.\n")); return False; } if (!posix_pending_close_tdb) posix_pending_close_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL, read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644); if (!posix_pending_close_tdb) { DEBUG(0,("Failed to open POSIX pending close database.\n")); return False; } return True;}/******************************************************************* Delete the in-memory POSIX lock databases.********************************************************************/BOOL posix_locking_end(void){ if (posix_lock_tdb && tdb_close(posix_lock_tdb) != 0) return False; if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0) return False; return True;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -