diff options
Diffstat (limited to 'source3/libsmb/smb_share_modes.c')
-rw-r--r-- | source3/libsmb/smb_share_modes.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/source3/libsmb/smb_share_modes.c b/source3/libsmb/smb_share_modes.c new file mode 100644 index 0000000000..0ba68aed06 --- /dev/null +++ b/source3/libsmb/smb_share_modes.c @@ -0,0 +1,452 @@ +/* + Samba share mode database library external interface library. + Used by non-Samba products needing access to the Samba share mode db. + + Copyright (C) Jeremy Allison 2005. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "includes.h" +#include "smb_share_modes.h" + +/* + * open/close sharemode database. + */ + +struct smbdb_ctx *smb_share_mode_db_open(const char *db_path) +{ + struct smbdb_ctx *smb_db = SMB_MALLOC_P(struct smbdb_ctx); + + if (!smb_db) { + return NULL; + } + + memset(smb_db, '\0', sizeof(struct smbdb_ctx)); + + smb_db->smb_tdb = tdb_open_log(lock_path("locking.tdb"), + 0, TDB_DEFAULT|TDB_CLEAR_IF_FIRST, + O_RDWR|O_CREAT, + 0644); + + if (!smb_db->smb_tdb) { + free(smb_db); + return NULL; + } + + /* Should check that this is the correct version.... */ + return smb_db; +} + +int smb_share_mode_db_close(struct smbdb_ctx *db_ctx) +{ + int ret = tdb_close(db_ctx->smb_tdb); + free(db_ctx); + return ret; +} + +/* Create locking key. */ + +struct samba_locking_key { + SMB_DEV_T dev; + SMB_INO_T ino; +}; + +static TDB_DATA get_locking_key(uint64_t dev, uint64_t ino) +{ + static struct samba_locking_key lk; + TDB_DATA ld; + + memset(&lk, '\0', sizeof(struct samba_locking_key)); + lk.dev = (SMB_DEV_T)dev; + lk.ino = (SMB_INO_T)ino; + ld.dptr = (char *)&lk; + ld.dsize = sizeof(lk); + return ld; +} + +/* + * lock/unlock entry in sharemode database. + */ + +int smb_lock_share_mode_entry(struct smbdb_ctx *db_ctx, + uint64_t dev, + uint64_t ino) +{ + return tdb_chainlock(db_ctx->smb_tdb, get_locking_key(dev, ino)); +} + +int smb_unlock_share_mode_entry(struct smbdb_ctx *db_ctx, + dev_t dev, + ino_t ino) +{ + return tdb_chainunlock(db_ctx->smb_tdb, get_locking_key(dev, ino)); +} + +/* Internal structure of Samba share mode db. */ +/* FIXME ! This should be moved into a Samba include file. */ + +struct locking_data { + union { + struct { + int num_share_mode_entries; + BOOL delete_on_close; + } s; + share_mode_entry dummy; /* Needed for alignment. */ + } u; + /* the following two entries are implicit + share_mode_entry modes[num_share_mode_entries]; + char file_name[]; + */ +}; + +/* + * Check if an external smb_share_mode_entry and an internal share_mode entry match. + */ + +static int share_mode_entry_equal(const struct smb_share_mode_entry *e_entry, const share_mode_entry *entry) +{ + return (e_entry->pid == entry->pid && + e_entry->file_id == (uint32_t)entry->share_file_id && + e_entry->open_time.tv_sec == entry->time.tv_sec && + e_entry->open_time.tv_usec == entry->time.tv_usec && + e_entry->share_access == (uint32_t)entry->share_access && + e_entry->access_mask == (uint32_t)entry->access_mask && + e_entry->dev == (uint64_t)entry->dev && + e_entry->ino == (uint64_t)entry->inode); +} + +/* + * Create an internal Samba share_mode entry from an external smb_share_mode_entry. + */ + +static void create_share_mode_entry(share_mode_entry *out, const struct smb_share_mode_entry *in) +{ + memset(out, '\0', sizeof(share_mode_entry)); + + out->pid = in->pid; + out->share_file_id = (unsigned long)in->file_id; + out->time.tv_sec = in->open_time.tv_sec; + out->time.tv_usec = in->open_time.tv_usec; + out->share_access = in->share_access; + out->access_mask = in->access_mask; + out->dev = (SMB_DEV_T)in->dev; + out->inode = (SMB_INO_T)in->ino; +} + +/* + * Return the current share mode list for an open file. + * This uses similar (but simplified) logic to locking/locking.c + */ + +int smb_get_share_mode_entries(struct smbdb_ctx *db_ctx, + uint64_t dev, + uint64_t ino, + struct smb_share_mode_entry **pp_list, + unsigned char *p_delete_on_close) +{ + TDB_DATA db_data; + struct smb_share_mode_entry *list = NULL; + int num_share_modes = 0; + struct locking_data *ld = NULL; /* internal samba db state. */ + share_mode_entry *shares = NULL; + size_t i; + + *pp_list = NULL; + *p_delete_on_close = 0; + + db_data = tdb_fetch(db_ctx->smb_tdb, get_locking_key(dev, ino)); + if (!db_data.dptr) { + return 0; + } + + ld = (struct locking_data *)db_data.dptr; + num_share_modes = ld->u.s.num_share_mode_entries; + + if (!num_share_modes) { + free(db_data.dptr); + return 0; + } + + list = SMB_MALLOC_ARRAY(struct smb_share_mode_entry, num_share_modes); + if (!list) { + free(db_data.dptr); + return -1; + } + + memset(list, '\0', num_share_modes * sizeof(struct smb_share_mode_entry)); + + shares = (share_mode_entry *)(db_data.dptr + sizeof(struct locking_data)); + + for (i = 0; i < num_share_modes; i++) { + share_mode_entry *share = &shares[i]; + struct smb_share_mode_entry *sme = &list[i]; + pid_t pid = share->pid; + + /* Check this process really exists. */ + if (kill(pid, 0) == -1 && (errno == ESRCH)) { + continue; /* No longer exists. */ + } + + /* Copy into the external list. */ + sme->dev = (uint64_t)share->dev; + sme->ino = (uint64_t)share->inode; + sme->share_access = (uint32_t)share->share_access; + sme->access_mask = (uint32_t)share->access_mask; + sme->open_time.tv_sec = share->time.tv_sec; + sme->open_time.tv_usec = share->time.tv_usec; + sme->file_id = (uint32_t)share->share_file_id; + sme->pid = share->pid; + } + + if (i == 0) { + free(db_data.dptr); + free(list); + return 0; + } + + *p_delete_on_close = ld->u.s.delete_on_close; + *pp_list = list; + return i; +} + +/* + * Create an entry in the Samba share mode db. + */ + +int smb_create_share_mode_entry(struct smbdb_ctx *db_ctx, + uint64_t dev, + uint64_t ino, + const struct smb_share_mode_entry *new_entry, + const char *filename) /* Must be abolute utf8 path. */ +{ + TDB_DATA db_data; + TDB_DATA locking_key = get_locking_key(dev, ino); + int orig_num_share_modes = 0; + struct locking_data *ld = NULL; /* internal samba db state. */ + share_mode_entry *shares = NULL; + char *new_data_p = NULL; + size_t new_data_size = 0; + + db_data = tdb_fetch(db_ctx->smb_tdb, locking_key); + if (!db_data.dptr) { + /* We must create the entry. */ + db_data.dptr = SMB_MALLOC(sizeof(struct locking_data) + sizeof(share_mode_entry) + strlen(filename) + 1); + if (!db_data.dptr) { + return -1; + } + ld = (struct locking_data *)db_data.dptr; + ld->u.s.num_share_mode_entries = 1; + ld->u.s.delete_on_close = 0; + shares = (share_mode_entry *)db_data.dptr + sizeof(struct locking_data); + create_share_mode_entry(shares, new_entry); + memcpy(db_data.dptr + sizeof(struct locking_data) + sizeof(share_mode_entry), + filename, + strlen(filename) + 1); + + db_data.dsize = sizeof(struct locking_data) + sizeof(share_mode_entry) + strlen(filename) + 1; + if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_INSERT) == -1) { + free(db_data.dptr); + return -1; + } + free(db_data.dptr); + return 0; + } + + /* Entry exists, we must add a new entry. */ + new_data_p = SMB_MALLOC(db_data.dsize + sizeof(share_mode_entry)); + if (!new_data_p) { + free(db_data.dptr); + return -1; + } + + ld = (struct locking_data *)db_data.dptr; + orig_num_share_modes = ld->u.s.num_share_mode_entries; + + /* Copy the original data. */ + memcpy(new_data_p, db_data.dptr, sizeof(struct locking_data) + (orig_num_share_modes*sizeof(share_mode_entry))); + + /* Add in the new share mode */ + shares = (share_mode_entry *)(db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes*sizeof(share_mode_entry))); + create_share_mode_entry(shares, new_entry); + + ld = (struct locking_data *)new_data_p; + ld->u.s.num_share_mode_entries++; + + /* Append the original filename */ + memcpy(new_data_p + sizeof(struct locking_data) + (ld->u.s.num_share_mode_entries * sizeof(share_mode_entry)), + db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(share_mode_entry)), + db_data.dsize - (sizeof(struct locking_data) + (orig_num_share_modes * sizeof(share_mode_entry)))); + + new_data_size = db_data.dsize + sizeof(share_mode_entry); + + free(db_data.dptr); + + db_data.dptr = new_data_p; + db_data.dsize = new_data_size; + + if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) { + free(db_data.dptr); + return -1; + } + free(db_data.dptr); + return 0; +} + +int smb_delete_share_mode_entry(struct smbdb_ctx *db_ctx, + uint64_t dev, + uint64_t ino, + const struct smb_share_mode_entry *del_entry) +{ + TDB_DATA db_data; + TDB_DATA locking_key = get_locking_key(dev, ino); + int orig_num_share_modes = 0; + struct locking_data *ld = NULL; /* internal samba db state. */ + share_mode_entry *shares = NULL; + char *new_data_p = NULL; + size_t filename_size = 0; + size_t i; + + db_data = tdb_fetch(db_ctx->smb_tdb, locking_key); + if (!db_data.dptr) { + return -1; /* Error - missing entry ! */ + } + + ld = (struct locking_data *)db_data.dptr; + orig_num_share_modes = ld->u.s.num_share_mode_entries; + shares = (share_mode_entry *)(db_data.dptr + sizeof(struct locking_data)); + + if (orig_num_share_modes == 1) { + /* Only one entry - better be ours... */ + if (!share_mode_entry_equal(del_entry, shares)) { + /* Error ! We can't delete someone else's entry ! */ + free(db_data.dptr); + return -1; + } + /* It's ours - just remove the entire record. */ + free(db_data.dptr); + return tdb_delete(db_ctx->smb_tdb, locking_key); + } + + /* More than one - allocate a new record minus the one we'll delete. */ + new_data_p = SMB_MALLOC(db_data.dsize - sizeof(share_mode_entry)); + if (!new_data_p) { + free(db_data.dptr); + return -1; + } + + /* Copy the header. */ + memcpy(new_data_p, db_data.dptr, sizeof(struct locking_data)); + + for (i = 0; i < orig_num_share_modes; i++) { + share_mode_entry *share = &shares[i]; + pid_t pid = share->pid; + + /* Check this process really exists. */ + if (kill(pid, 0) == -1 && (errno == ESRCH)) { + continue; /* No longer exists. */ + } + + if (share_mode_entry_equal(del_entry, share)) { + continue; /* This is our delete taget. */ + } + + memcpy(new_data_p + sizeof(struct locking_data) + (i*sizeof(share_mode_entry)), + share, sizeof(share_mode_entry) ); + } + + if (i == 0) { + /* None left after pruning. Delete record. */ + free(db_data.dptr); + free(new_data_p); + return tdb_delete(db_ctx->smb_tdb, locking_key); + } + + /* Copy the terminating filename. */ + filename_size = db_data.dsize - ( sizeof(struct locking_data) + (orig_num_share_modes * sizeof(share_mode_entry))); + + memcpy(new_data_p + sizeof(struct locking_data) + (i*sizeof(share_mode_entry)), + db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(share_mode_entry)), + filename_size); + + free(db_data.dptr); + + db_data.dptr = new_data_p; + + /* Re-save smaller record. */ + ld = (struct locking_data *)db_data.dptr; + ld->u.s.num_share_mode_entries = i; + + db_data.dsize = sizeof(struct locking_data) + (i*sizeof(share_mode_entry)) + filename_size; + + if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) { + free(db_data.dptr); + return -1; + } + free(db_data.dptr); + return 0; +} + +int smb_change_share_mode_entry(struct smbdb_ctx *db_ctx, + uint64_t dev, + uint64_t ino, + const struct smb_share_mode_entry *set_entry, + const struct smb_share_mode_entry *new_entry) +{ + TDB_DATA db_data; + TDB_DATA locking_key = get_locking_key(dev, ino); + int num_share_modes = 0; + struct locking_data *ld = NULL; /* internal samba db state. */ + share_mode_entry *shares = NULL; + size_t i; + int found_entry = 0; + + db_data = tdb_fetch(db_ctx->smb_tdb, locking_key); + if (!db_data.dptr) { + return -1; /* Error - missing entry ! */ + } + + ld = (struct locking_data *)db_data.dptr; + num_share_modes = ld->u.s.num_share_mode_entries; + shares = (share_mode_entry *)(db_data.dptr + sizeof(struct locking_data)); + + for (i = 0; i < num_share_modes; i++) { + share_mode_entry *share = &shares[i]; + pid_t pid = share->pid; + + /* Check this process really exists. */ + if (kill(pid, 0) == -1 && (errno == ESRCH)) { + continue; /* No longer exists. */ + } + + if (share_mode_entry_equal(set_entry, share)) { + create_share_mode_entry(share, new_entry); + break; + } + } + + if (!found_entry) { + free(db_data.dptr); + return -1; + } + + /* Save modified data. */ + if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) { + free(db_data.dptr); + return -1; + } + free(db_data.dptr); + return 0; +} |