diff options
Diffstat (limited to 'source3/lib/username.c')
-rw-r--r-- | source3/lib/username.c | 272 |
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 |