summaryrefslogtreecommitdiff
path: root/source3/lib/username.c
diff options
context:
space:
mode:
authorLuke Leighton <lkcl@samba.org>1999-05-06 18:05:45 +0000
committerLuke Leighton <lkcl@samba.org>1999-05-06 18:05:45 +0000
commit150645f955d1f15b0ea43069020070424f774521 (patch)
tree392a53016e1af6844066696570b2180344578744 /source3/lib/username.c
parentc6e5eb5bc29565da4a1b5d528696b35d5887df7d (diff)
downloadsamba-150645f955d1f15b0ea43069020070424f774521.tar.gz
samba-150645f955d1f15b0ea43069020070424f774521.tar.bz2
samba-150645f955d1f15b0ea43069020070424f774521.zip
Jani Jaakkola's "getpwuid() / getpwnam()" hash-cache-hack
(This used to be commit 899fc053c50448db65092d9f25fea99433cfb29f)
Diffstat (limited to 'source3/lib/username.c')
-rw-r--r--source3/lib/username.c272
1 files changed, 271 insertions, 1 deletions
diff --git a/source3/lib/username.c b/source3/lib/username.c
index 8ae55fcc89..4daf30fdd4 100644
--- a/source3/lib/username.c
+++ b/source3/lib/username.c
@@ -27,6 +27,276 @@ static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (
static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (char *), int N);
/****************************************************************************
+ Since getpwnam() makes samba really slow with the NT-domain code
+ (reading /etc/passwd again and again and again), here is an implementation
+ of very simple passwd cache
+****************************************************************************/
+#define PASSWD_HASH_SIZE 1009
+/* The hashtable is rebuild every 15 seconds */
+#define PASSWD_HASH_AGE 15
+struct passwd_hash_entry {
+ int entry;
+ int next;
+};
+
+struct passwd_hash_table_s {
+ struct passwd *passwds;
+ int passwds_size;
+ int *names;
+ int *uids;
+ struct passwd_hash_entry *entries;
+ int entries_size;
+ struct timeval build_time;
+} passwd_hash_table = {
+ NULL,0,NULL,NULL,NULL,0,{0,0}
+};
+
+int name_hash_function(const char *name)
+{
+ /* I guess that there must be better hash functions. This one was the
+ * first to come into mind :) */
+ unsigned int value=0;
+ while (*name) {
+ value=(value<<8)|(unsigned char)(*name);
+ if (value>1048576) value=value%PASSWD_HASH_SIZE;
+ name++;
+ }
+ value=value%PASSWD_HASH_SIZE;
+ return value;
+}
+
+int uid_hash_function(uid_t uid)
+{
+ return uid%PASSWD_HASH_SIZE;
+}
+
+
+BOOL build_passwd_hash_table()
+{
+ struct passwd_hash_table_s *pht=&passwd_hash_table; /* Convenience */
+ int num_passwds=0;
+ int num_entries=0;
+ struct passwd *pass;
+ int i;
+ int name_i,uid_i;
+
+ DEBUG(3,("Building passwd hash table\n"));
+ /* Free the allocated strings in old hash table */
+ for (i=0;i<pht->passwds_size;i++) {
+ free(pht->passwds[i].pw_name);
+ free(pht->passwds[i].pw_passwd);
+ free(pht->passwds[i].pw_gecos);
+ free(pht->passwds[i].pw_dir);
+ free(pht->passwds[i].pw_shell);
+ }
+
+ /* Initialize hash table if first table build */
+ if (pht->passwds_size==0) {
+ DEBUG(3,("Building passwd hash table for the first time\n"));
+ pht->passwds=malloc(sizeof(struct passwd)*64); /* A reasonable default */
+ pht->passwds_size=64;
+ }
+ if (pht->names==NULL) {
+ pht->names=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
+ }
+ if (pht->uids==NULL) {
+ pht->uids=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
+ }
+ if (pht->entries==NULL) {
+ pht->entries=malloc(sizeof(struct passwd_hash_entry)*128);
+ pht->entries_size=128;
+ }
+ if (pht->passwds==NULL || pht->names==NULL ||
+ pht->uids==NULL || pht->entries==NULL) {
+ goto fail;
+ }
+
+ /* Clear out the hash table */
+ for(i=0;i<PASSWD_HASH_SIZE;i++) pht->uids[i]=-1;
+ for(i=0;i<PASSWD_HASH_SIZE;i++) pht->names[i]=-1;
+
+ /* Now do the build */
+ setpwent();
+
+ while((pass=getpwent())) {
+
+ /* Check that we have enough space */
+ if (num_passwds==pht->passwds_size) {
+ struct passwd *new_passwds=NULL;
+ pht->passwds_size+=pht->passwds_size/2;
+ new_passwds=realloc(pht->passwds,
+ sizeof(struct passwd)*pht->passwds_size);
+ if (new_passwds==NULL) goto fail;
+ pht->passwds=new_passwds;
+ }
+ if (num_entries+1>=pht->entries_size) {
+ pht->entries_size+=pht->entries_size/2;
+ pht->entries=realloc(pht->entries,
+ sizeof(struct passwd_hash_entry)*pht->entries_size);
+ if (pht->entries==NULL) goto fail;
+ }
+
+ /* Copy the passwd struct */
+ memset(&pht->passwds[num_passwds],0,sizeof(struct passwd));
+ pht->passwds[num_passwds].pw_uid=pass->pw_uid;
+ pht->passwds[num_passwds].pw_gid=pass->pw_gid;
+ if (
+ (pht->passwds[num_passwds].pw_name=strdup(pass->pw_name))==NULL ||
+ (pht->passwds[num_passwds].pw_passwd=strdup(pass->pw_passwd))==NULL ||
+ (pht->passwds[num_passwds].pw_gecos=strdup(pass->pw_gecos))==NULL ||
+ (pht->passwds[num_passwds].pw_dir=strdup(pass->pw_dir))==NULL ||
+ (pht->passwds[num_passwds].pw_shell=strdup(pass->pw_shell))==NULL ) {
+ num_passwds++;
+ goto fail;
+ }
+
+ /* Add to the hash table */
+ /* Add the name */
+ pht->entries[num_entries].entry=num_passwds;
+ name_i=name_hash_function(pass->pw_name);
+ pht->entries[num_entries].next=pht->names[name_i];
+ pht->names[name_i]=num_entries;
+ num_entries++;
+ /* Add the uid */
+ pht->entries[num_entries].entry=num_passwds;
+ uid_i=uid_hash_function(pass->pw_uid);
+ pht->entries[num_entries].next=pht->uids[uid_i];
+ pht->uids[uid_i]=num_entries;
+ num_entries++;
+
+ /* This entry has been done */
+ num_passwds++;
+ }
+ endpwent();
+
+ if (pht->passwds_size>num_passwds) {
+ struct passwd *passwds;
+ passwds=realloc(pht->passwds,sizeof(pht->passwds[0])*num_passwds);
+ if (passwds==NULL) goto fail;
+ pht->passwds=passwds;
+ pht->passwds_size=num_passwds;
+ }
+ if (pht->entries_size>num_entries) {
+ struct passwd_hash_entry *entries;
+ entries=realloc(pht->entries,sizeof(pht->entries[0])*num_entries);
+ if (entries==NULL) goto fail;
+ pht->entries=entries;
+ pht->entries_size=num_entries;
+ }
+
+ /* Mark the creation time */
+ GetTimeOfDay(&pht->build_time);
+ /* Everything went smoothly. */
+ return True;
+
+ fail:
+ DEBUG(0,("Failed to create passwd hash table: %s",strerror(errno)));
+ /* OK: now the untested part. Normally this should never happen:
+ * Only running out of memory could cause this and even then
+ * we have enough trouble already. */
+ while (num_passwds>0) {
+ num_passwds--;
+ free(pht->passwds[num_passwds].pw_name);
+ free(pht->passwds[num_passwds].pw_passwd);
+ free(pht->passwds[num_passwds].pw_gecos);
+ free(pht->passwds[num_passwds].pw_dir);
+ free(pht->passwds[num_passwds].pw_shell);
+ }
+ free(pht->entries);
+ free(pht->uids);
+ free(pht->names);
+ free(pht->passwds);
+ pht->passwds_size=0;
+ pht->entries_size=0;
+ /* Also mark fail time, so that retry will happen after PASSWD_HASH_AGE */
+ GetTimeOfDay(&pht->build_time);
+ return False;
+}
+
+BOOL have_passwd_hash() {
+ struct passwd_hash_table_s *pht=&passwd_hash_table;
+ struct timeval tv;
+ GetTimeOfDay(&tv);
+ /* I'm ignoring microseconds. If you think they matter, go ahead
+ * and implement them */
+ if (tv.tv_sec - pht->build_time.tv_sec > PASSWD_HASH_AGE) {
+ return build_passwd_hash_table();
+ }
+ return pht->passwds_size>0;
+}
+
+struct passwd *hashed_getpwnam(const char *name)
+{
+ struct passwd_hash_table_s *pht=&passwd_hash_table;
+
+ DEBUG(5,("getpwnam(%s)\n", name));
+
+ if (have_passwd_hash()) {
+ int name_i=name_hash_function(name);
+ int index=pht->names[name_i];
+ while(index!=-1) {
+ struct passwd *pass=&pht->passwds[pht->entries[index].entry];
+ if (strcmp(name,pass->pw_name)==0) {
+ DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
+ pass->pw_name,
+ pass->pw_passwd,
+ pass->pw_uid,
+ pass->pw_gid,
+ pass->pw_gecos,
+ pass->pw_dir,
+ pass->pw_shell));
+ return pass;
+ }
+ index=pht->entries[index].next;
+ }
+
+ /* Not found */
+ DEBUG(5,("%s not found\n",name));
+ return NULL;
+ }
+ /* Fall back to real getpwnam() */
+ return getpwnam(name);
+}
+
+/*******************************************************************
+turn a uid into a user name
+********************************************************************/
+char *uidtoname(uid_t uid)
+{
+ static char name[40];
+ struct passwd_hash_table_s *pht=&passwd_hash_table;
+ struct passwd *pass=NULL;
+
+ DEBUG(5,("uidtoname(%d)\n",uid));
+ if (have_passwd_hash()) {
+ int index=pht->uids[uid_hash_function(uid)];
+ while(index!=-1) {
+ pass=&pht->passwds[pht->entries[index].entry];
+ if (pass->pw_uid==uid) {
+ DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
+ pass->pw_name,
+ pass->pw_passwd,
+ pass->pw_uid,
+ pass->pw_gid,
+ pass->pw_gecos,
+ pass->pw_dir,
+ pass->pw_shell));
+ return pass->pw_name;
+ }
+ index=pht->entries[index].next;
+ }
+ DEBUG(5,("Hash miss"));
+ pass=NULL;
+ } else {
+ /* No hash table, fall back to getpwuid */
+ pass = getpwuid(uid);
+ }
+ if (pass) return(pass->pw_name);
+ slprintf(name, sizeof(name) - 1, "%d",(int)uid);
+ return(name);
+}
+
+/****************************************************************************
get a users home directory.
****************************************************************************/
char *get_home_dir(char *user)
@@ -154,7 +424,7 @@ static struct passwd *_Get_Pwnam(char *s)
{
struct passwd *ret;
- ret = getpwnam(s);
+ ret = hashed_getpwnam(s);
if (ret)
{
#ifdef HAVE_GETPWANAM