diff -r 000000000000 -r c045670f36e9 queue-fix.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/queue-fix.c Thu Nov 01 14:46:11 2007 +0100 @@ -0,0 +1,757 @@ +/* + queue-fix 1.4 + by Eric Huss + e-huss@netmeridian.com + + reconstructs qmail's queue +*/ +#include +#include +#include +#include +#include "stralloc.h" +#include "direntry.h" +#include "fmt.h" +#include "error.h" +#include "subfd.h" +#include "getln.h" +#include "str.h" +#include "open.h" +#include "fifo.h" +#include "scan.h" + +/*change this to your qmail's conf-split value*/ +#define SPLIT_NUM 23 + +stralloc queue_dir = {0}; /*the root queue dir with trailing slash*/ +stralloc check_dir = {0}; /*the current directory being checked*/ +stralloc temp_filename = {0}; /*temporary used for checking individuals*/ +stralloc temp_dirname = {0}; /*temporary used for checking individuals*/ +stralloc old_name = {0}; /*used in rename*/ +stralloc new_name = {0}; /*used in rename*/ +stralloc mess_dir = {0}; /*used for renaming in mess dir*/ +stralloc query = {0}; /*used in interactive query function*/ + +char name_num[FMT_ULONG]; +int flag_interactive=0; +int flag_doit=1; +int flag_dircreate=0; +int flag_filecreate=0; +int flag_permfix=0; +int flag_namefix=0; +int flag_unlink=0; + +int qmailq_uid; +int qmails_uid; +int qmailr_uid; +int qmail_gid; + +void die_make(char * name) +{ +printf("Failed to make %s\n" + "\nExiting...\n\n",name); +exit(1); +} + +void die_user(char * user) +{ +printf("Failed to determine the uid of %s\n" + "\nExiting...\n\n",user); +exit(1); +} + +void die_group(char * group) +{ +printf("Failed to determine the gid of %s\n" + "\nExiting...\n\n",group); +exit(1); +} + +void die_args() +{ +printf("Invalid arguments.\n" + "queue-fix [-i | -N] queue_dir\n" + "\nExiting...\n\n"); +exit(1); +} + +void die_check() +{ +printf("Failed while checking directory structure.\n" + "Make sure the given queue exists and you have permission to access it.\n" + "\nExiting...\n\n"); +exit(1); +} + +void die_recon() +{ +printf("Failed to reconstruct queue.\n" + "Make sure the queue exists and you have permission to modify it.\n" + "\nExiting...\n\n"); +exit(1); +} + +void die_nomem() +{ +printf("Hmm.. Out of memory\n" + "\nExiting...\n\n"); +exit(1); +} + +void die_rerun() +{ +printf(".tmp files exist in the queue.\n" + "queue-fix may have abnormally terminated in a previous run.\n" + "The queue must be manually cleaned of the .tmp files.\n" + "\nExiting...\n\n"); +exit(1); +} + +/*returns 1==yes, 0==no*/ +int confirm() +{ +int match; + + if(getln(subfdinsmall,&query,&match,'\n')) return 0; + if(!match) return 0; + if(query.s[0]=='y' || query.s[0]=='Y' || query.s[0]=='\n') return 1; + return 0; +} + +/*gid may be -1 on files for "unknown*/ +int check_item(char * name,int uid,int gid,int perm,char type,int size) +{ +struct stat st; +int fd; + + /*check for existence and proper credentials*/ + switch(type) { + case 'd': /*directory*/ + if(stat(name,&st)) { + if(errno!=error_noent) return -1; + if(!flag_dircreate && flag_interactive) { + printf("It looks like some directories don't exist, should I create them? (Y/n)\n"); + if(!confirm()) return -1; + flag_dircreate = 1; + } + /*create it*/ + printf("Creating directory [%s]\n",name); + if(flag_doit) if(mkdir(name,perm)) die_make(name); + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if(st.st_uid!=uid || st.st_gid!=gid) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + } + if((st.st_mode & 07777) != perm) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + } + return 0; + case 'f': /*regular file*/ + if(stat(name,&st)) return -1; + /*check the values*/ + if(st.st_uid!=uid || (st.st_gid!=gid && gid!=-1)) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + } + if((st.st_mode & 07777) != perm) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + } + return 0; + case 'z': /*regular file with a size*/ + if(stat(name,&st)) { + if(errno!=error_noent) return -1; + if(!flag_filecreate && flag_interactive) { + printf("It looks like some files don't exist, should I create them? (Y/n)\n"); + if(!confirm()) return -1; + flag_filecreate = 1; + } + /*create it*/ + printf("Creating [%s] with size [%u]\n",name,size); + if(flag_doit) { + fd = open_trunc(name); + if(fd==-1) die_make(name); + while(size--) { + if(write(fd,"",1)!=1) die_make(name); + } + close(fd); + } + + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if(st.st_uid!=uid || (st.st_gid!=gid && gid!=-1)) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + } + if((st.st_mode & 07777) != perm) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + } + if(st.st_size!=size) { + printf("%s is not the right size. I will not fix it, please investigate.\n",name); + } + return 0; + case 'p': /*a named pipe*/ + if(stat(name,&st)) { + if(errno!=error_noent) return -1; + if(!flag_filecreate && flag_interactive) { + printf("It looks like some files don't exist, should I create them? (Y/n)\n"); + if(!confirm()) return -1; + flag_filecreate = 1; + } + /*create it*/ + printf("Creating fifo [%s]\n",name); + if(flag_doit) if(fifo_make(name,perm)) die_make(name); + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + return 0; + } + /*check the values*/ + if(st.st_uid!=uid || (st.st_gid!=gid && gid!=-1)) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing ownership of [%s] to uid %u gid %u\n",name,uid,gid); + if(flag_doit) if(chown(name,uid,gid)) die_make(name); + } + if((st.st_mode & 07777) != perm) { + if(!flag_permfix && flag_interactive) { + printf("It looks like some permissions are wrong, should I fix them? (Y/n)\n"); + if(!confirm()) return -1; + flag_permfix = 1; + } + /*fix it*/ + printf("Changing permissions of [%s] to [%o]\n",name,perm); + if(flag_doit) if(chmod(name,perm)) die_make(name); + } + return 0; + } /*end switch*/ + + return 0; +} + +int check_files(char * directory, int uid, int gid, int perm) +{ +DIR * dir; +direntry * d; + + dir = opendir(directory); + if(!dir) return -1; + while((d = readdir(dir))) { + if(d->d_name[0]=='.') continue; + if(!stralloc_copys(&temp_filename,directory)) die_nomem(); + if(!stralloc_append(&temp_filename,"/")) die_nomem(); + if(!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if(!stralloc_0(&temp_filename)) die_nomem(); + if(check_item(temp_filename.s,uid,gid,perm,'f',0)) { closedir(dir); return -1; } + } + closedir(dir); + return 0; +} + +void warn_files(char * directory) +{ +DIR * dir; +direntry * d; +int found = 0; + + dir = opendir(directory); + if(!dir) return; + + while((d = readdir(dir))) { + if(d->d_name[0]=='.') continue; + found = 1; + break; + } + + closedir(dir); + + if(found) { + printf("Found files in %s that shouldn't be there.\n" + "I will not remove them. You should consider checking it out.\n\n",directory); + } +} + +int check_splits(char * directory, int dir_uid, int dir_gid, int dir_perm, + int file_gid, int file_perm) +{ +DIR * dir; +direntry * d; +int i; + + for(i=0;id_name[0]=='.') continue; + if(!stralloc_copy(&temp_filename,&temp_dirname)) die_nomem(); + temp_filename.len--; /*remove NUL*/ + if(!stralloc_append(&temp_filename,"/")) die_nomem(); + if(!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if(!stralloc_0(&temp_filename)) die_nomem(); + if(check_item(temp_filename.s,dir_uid,file_gid,file_perm,'f',0)) { closedir(dir); return -1; } + } + closedir(dir); + } /*end for*/ + return 0; +} + +int rename_mess(char * dir, char * part, char * new_part, char * old_filename, char * new_filename) +{ +struct stat st; + + if(flag_interactive && !flag_namefix) { + printf("It looks like some files need to be renamed, should I rename them? (Y/n)\n"); + if(!confirm()) return -1; + flag_namefix = 1; + } + /*prepare the old filename*/ + if(!stralloc_copy(&old_name,&queue_dir)) die_nomem(); + if(!stralloc_cats(&old_name,dir)) die_nomem(); + if(!stralloc_cats(&old_name,part)) die_nomem(); + if(!stralloc_append(&old_name,"/")) die_nomem(); + if(!stralloc_cats(&old_name,old_filename)) die_nomem(); + if(!stralloc_0(&old_name)) die_nomem(); + + /*prepare the new filename*/ + if(!stralloc_copy(&new_name,&queue_dir)) die_nomem(); + if(!stralloc_cats(&new_name,dir)) die_nomem(); + if(!stralloc_cats(&new_name,new_part)) die_nomem(); + if(!stralloc_append(&new_name,"/")) die_nomem(); + if(!stralloc_cats(&new_name,new_filename)) die_nomem(); + if(!stralloc_0(&new_name)) die_nomem(); + + /*check if destination exists*/ + if(stat(new_name.s,&st)==0) { + /*it exists*/ + new_name.len--; /*remove NUL*/ + /*append an extension to prevent name clash*/ + if(!stralloc_cats(&new_name,".tmp")) die_nomem(); + if(!stralloc_0(&new_name)) die_nomem(); + /*do a double check for collision*/ + if(stat(new_name.s,&st)==0) die_rerun(); + } + + printf("Renaming [%s] to [%s]\n",old_name.s,new_name.s); + if(flag_doit) { + if(rename(old_name.s,new_name.s)) { + if(errno!=error_noent) { + return -1; + } + } + } + return 0; +} + +int fix_part(char * part,int part_num) +{ +DIR * dir; +direntry * d; +struct stat st; +char inode[FMT_ULONG]; +char new_part[FMT_ULONG]; +int old_inode; +int correct_part_num; + + if(!stralloc_copy(&mess_dir,&queue_dir)) die_nomem(); + if(!stralloc_cats(&mess_dir,"mess/")) die_nomem(); + if(!stralloc_cats(&mess_dir,part)) die_nomem(); + if(!stralloc_0(&mess_dir)) die_nomem(); + + dir = opendir(mess_dir.s); + if(!dir) return -1; + + while((d = readdir(dir))) { + if(d->d_name[0]=='.') continue; + /*check from mess*/ + if(!stralloc_copy(&temp_filename,&mess_dir)) die_nomem(); + temp_filename.len--; /*remove NUL*/ + if(!stralloc_append(&temp_filename,"/")) die_nomem(); + if(!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if(!stralloc_0(&temp_filename)) die_nomem(); + if(stat(temp_filename.s,&st)) { closedir(dir); return -1; } + + /*check that filename==inode number*/ + /*check that inode%auto_split==part_num*/ + scan_ulong(d->d_name,&old_inode); + correct_part_num = st.st_ino % SPLIT_NUM; + if(st.st_ino != old_inode || + part_num!=correct_part_num) { + /*rename*/ + inode[fmt_ulong(inode,st.st_ino)]=0; + new_part[fmt_ulong(new_part,correct_part_num)]=0; + if(rename_mess("mess/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if(rename_mess("info/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if(rename_mess("local/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + if(rename_mess("remote/",part,new_part,d->d_name,inode)) { closedir(dir); return -1; } + + if(rename_mess("intd","","",d->d_name,inode)) { closedir(dir); return -1; } + if(rename_mess("todo","","",d->d_name,inode)) { closedir(dir); return -1; } + if(rename_mess("bounce","","",d->d_name,inode)) { closedir(dir); return -1; } + } + } + + closedir(dir); + return 0; +} + +int clean_tmp(char * directory, char * part) +{ +DIR * dir; +direntry * d; +struct stat st; +int length; + + if(!stralloc_copy(&mess_dir,&queue_dir)) die_nomem(); + if(!stralloc_cats(&mess_dir,directory)) die_nomem(); + if(!stralloc_cats(&mess_dir,part)) die_nomem(); + if(!stralloc_0(&mess_dir)) die_nomem(); + + dir = opendir(mess_dir.s); + if(!dir) return -1; + + while((d = readdir(dir))) { + if(d->d_name[0]=='.') continue; + + /*check for tmp extension*/ + length = str_len(d->d_name); + if(length>4) { + if(str_equal(d->d_name+length-4,".tmp")) { + /*remove the extension*/ + if(!stralloc_copys(&temp_filename,d->d_name)) die_nomem(); + temp_filename.len-=4; + if(!stralloc_0(&temp_filename)) die_nomem(); + + if(rename_mess(directory,part,part,d->d_name,temp_filename.s)) { closedir(dir); return -1; } + } + } + } + + closedir(dir); + return 0; +} + +int fix_names() +{ +int i; + + if(!stralloc_copy(&check_dir,&queue_dir)) die_nomem(); + if(!stralloc_cats(&check_dir,"mess")) die_nomem(); + if(!stralloc_0(&check_dir)) die_nomem(); + + /*make the filenames match their inode*/ + for(i=0;id_name[0]=='.') continue; + + scan_ulong(d->d_name,&inode); + part = inode % SPLIT_NUM; + new_part[fmt_ulong(new_part,part)]=0; + + /*check for corresponding entry in mess dir*/ + if(!stralloc_copy(&mess_dir,&queue_dir)) die_nomem(); + if(!stralloc_cats(&mess_dir,"mess/")) die_nomem(); + if(!stralloc_cats(&mess_dir,new_part)) die_nomem(); + if(!stralloc_append(&mess_dir,"/")) die_nomem(); + if(!stralloc_cats(&mess_dir,d->d_name)) die_nomem(); + if(!stralloc_0(&mess_dir)) die_nomem(); + + if(stat(mess_dir.s,&st)) { + /*failed to find in mess*/ + if(flag_interactive && !flag_unlink) { + printf("There are some stray files in %s\n",directory); + printf("Should I remove them? (Y/n)\n"); + if(!confirm()) { + closedir(dir); + return -1; + } + flag_unlink = 1; + } + if(!stralloc_copys(&temp_filename,directory)) die_nomem(); + if(!stralloc_append(&temp_filename,"/")) die_nomem(); + if(!stralloc_cats(&temp_filename,d->d_name)) die_nomem(); + if(!stralloc_0(&temp_filename)) die_nomem(); + + printf("Unlinking [%s]\n",temp_filename.s); + if(flag_doit) if(unlink(temp_filename.s)==-1) { closedir(dir); return -1; } + } + } + + closedir(dir); + return 0; +} + +int check_stray_parts() +{ +int i; + + for(i=0;ipw_uid; + + pw = getpwnam("qmails"); + if(!pw) die_user("qmails"); + qmails_uid = pw->pw_uid; + + pw = getpwnam("qmailr"); + if(!pw) die_user("qmailr"); + qmailr_uid = pw->pw_uid; + + gr = getgrnam("qmail"); + if(!gr) die_group("qmail"); + qmail_gid = gr->gr_gid; + + /*check that all the proper directories exist with proper credentials*/ + if(check_dirs()) die_check(); + /*rename inode filenames*/ + if(fix_names()) die_check(); + /*check for stray files*/ + if(find_strays()) die_check(); + + printf("queue-fix finished...\n"); + return 0; +}