--- rm.c.orig 2006-10-31 11:22:36.000000000 +0900 +++ rm.c 2007-09-25 21:02:45.000000000 +0900 @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -57,17 +58,48 @@ #include #include +#define dFLAG 0x00000001 +#define fFLAG 0x00000002 +#define iFLAG 0x00000004 +#define PFLAG 0x00000008 +#define vFLAG 0x00000010 +#define WFLAG 0x00000020 +#define RFLAG 0x00000040 +#define IFLAG 0x00000080 +#define IGNORE_FLAG 0x80000000 + +struct rm_conf_list { + int flagtype; + int pathlen; + char path[PATH_MAX]; +}; + int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; int rflag, Iflag; +int confflags; uid_t uid; +int *pflags[] = { + &dflag, &fflag, &iflag, &Pflag, &vflag, &Wflag, &rflag, &Iflag +}; +#define FLAGS_LEN (sizeof(pflags) / sizeof(int*)) +int backupflags[FLAGS_LEN]; +int rcl_size; +struct rm_conf_list *rcl; +int add_conf(int, char *); int check(char *, char *, struct stat *); int check2(char **); void checkdot(char **); void checkslash(char **); +const char* dirname(const char*); +int load_conf(void); +const char* _realpath(const char*); +void restore_flags(void); void rm_file(char **); int rm_overwrite(char *, struct stat *); void rm_tree(char **); +void save_flags(void); +void set_flags(const char*); void usage(void); /* @@ -145,6 +177,8 @@ usage(); } + load_conf(); + checkdot(argv); if (getenv("POSIXLY_CORRECT") == NULL) checkslash(argv); @@ -163,9 +197,222 @@ rm_file(argv); } + if (rcl) + free(rcl); + exit (eval); } +int add_conf(int flagtype, char *path) +{ + struct rm_conf_list *ptr; + + if (!path || 0 >= strlen(path)) + return 1; + if (0 == flagtype) + flagtype = IGNORE_FLAG; + + if (!rcl) { + rcl = (struct rm_conf_list*)malloc(sizeof(struct rm_conf_list) *2); + if (!rcl) + return 1; + bzero((char*)rcl, sizeof(struct rm_conf_list) *2); + rcl_size = sizeof(struct rm_conf_list) *2; + } + else { + ptr = (struct rm_conf_list*)realloc(rcl, rcl_size + sizeof(struct rm_conf_list)); + if (!ptr) + return 1; + rcl = ptr; + bzero(((char*)rcl) + rcl_size, sizeof(struct rm_conf_list)); + rcl_size += sizeof(struct rm_conf_list); + } + + ptr = rcl + (rcl_size / sizeof(struct rm_conf_list)) -2; + ptr->flagtype = flagtype; + strlcpy(ptr->path, path, PATH_MAX); + ptr->pathlen = strlen(ptr->path); + + return 0; +} + +int load_conf(void) +{ + int ret; + int f_ok; + int dquote; + int flagtype; + char *home; + char confpath[PATH_MAX]; + char line[PATH_MAX + 64]; + char *p, *idx; + FILE *fp; + struct stat sb; + + rcl = NULL; + rcl_size = 0; + if (fflag) + return 0; + + bzero(confpath, sizeof(confpath)); + + if ((home = getenv("HOME")) == NULL) + return 1; + if (home[strlen(home)-1] == '/') + snprintf(confpath, sizeof(confpath) -1, "%s.rm", home); + else + snprintf(confpath, sizeof(confpath) -1, "%s/.rm", home); + + if (stat(confpath, &sb)) + return 1; + + /* check owner */ + if (sb.st_uid != getuid()) + return 1; + + /* check access mode */ + if (sb.st_mode & 022) + f_ok = 0; + else + f_ok = 1; + + /* load conf */ + if ((fp = fopen(confpath, "r")) == NULL) + return 1; + + ret = 0; + while (fgets(line, sizeof(line), fp) != NULL) { + dquote = 0; + flagtype = 0; + + for (p = line; '\0' != *p && isblank(*p); p++); + if ('#' == *p || '\0' == *p || isspace(*p)) + continue; + + for (; '\0' != p; p++) { + if (isblank(*p)) + break; + else if ('d' == *p) + flagtype |= dFLAG; + else if ('f' == *p) { + if (f_ok) + flagtype |= fFLAG; + } else if ('i' == *p) + flagtype |= iFLAG; + else if ('I' == *p) + flagtype |= IFLAG; + else if ('P' == *p) + flagtype |= PFLAG; +#if 0 + else if ('R' == *p || 'r' == *p) + flagtype |= RFLAG; +#endif + else if ('v' == *p) + flagtype |= vFLAG; + else if ('W' == *p) + flagtype |= WFLAG; + else if ('-' == *p) + flagtype |= IGNORE_FLAG; +#if 0 + else { + fclose(fp); + return 1; + } +#endif + } + for (; '\0' != *p && isblank(*p); p++); + if ('"' == *p) { + dquote = 1; + p++; + } + if ('/' != *p) + continue; + + if (dquote) { + if ((idx = rindex(p, '"')) == NULL) + continue; + *idx = '\n'; + } + else { + if ('\n' == p[strlen(p) -1]) + p[strlen(p) -1] = '\0'; + } + + if (add_conf(flagtype, p)) { + ret = 1; + break; + } + } + + fclose(fp); + + return ret; +} + +void +restore_flags(void) +{ + unsigned int count; + + for (count = 0; count < FLAGS_LEN; count++) { + *pflags[count] = backupflags[count]; + } +} + +void +save_flags(void) +{ + unsigned int count; + + for (count = 0; count < FLAGS_LEN; count++) { + backupflags[count] = *pflags[count]; + } +} + +void +set_flags(const char *path) +{ + int flagtype; + struct rm_conf_list *ptr; + + if (!rcl || !path || '\0' == path[0]) + return; + + flagtype = 0; + for (ptr = rcl; ptr->path; ptr++) { + if (strncmp(ptr->path, path, ptr->pathlen) == 0) { + flagtype = ptr->flagtype; + break; + } + } + + if (!flagtype || 0 != (IGNORE_FLAG & flagtype)) + return; + + if (flagtype & dFLAG) + dflag = 1; + if (flagtype & fFLAG) { + fflag = 1; + iflag = 0; + } + if (flagtype & iFLAG) { + iflag = 1; + fflag = 0; + } + if (flagtype & PFLAG) + Pflag = 1; + if (flagtype & vFLAG) + vflag = 1; + if (flagtype & WFLAG) + Wflag = 1; +#if 0 + if (flagtype & RFLAG) + rflag = 1; +#endif + if (flagtype & IFLAG) + Iflag = 1; +} + void rm_tree(char **argv) { @@ -197,7 +444,11 @@ return; err(1, "fts_open"); } + save_flags(); while ((p = fts_read(fts)) != NULL) { + restore_flags(); + if (FTS_ERR != p->fts_info && NULL != p->fts_accpath) + set_flags(dirname(_realpath(p->fts_accpath))); switch (p->fts_info) { case FTS_DNR: if (!fflag || p->fts_errno != ENOENT) { @@ -321,7 +572,10 @@ * Remove a file. POSIX 1003.2 states that, by default, attempting * to remove a directory is an error, so must always stat the file. */ + save_flags(); while ((f = *argv++) != NULL) { + restore_flags(); + set_flags(dirname(_realpath(f))); /* Assume if can't stat the file, can't unlink it. */ if (lstat(f, &sb)) { if (Wflag) { @@ -583,6 +837,83 @@ } } +const char* +dirname(const char* path) +{ + static char rpath[PATH_MAX]; + char *p; + + if (!path || (p = rindex(path, '/')) == NULL) + return path; + if (p == path) { + rpath[0] = '/'; + rpath[1] = '\0'; + } else { + if (PATH_MAX > (p - path +1)) + strlcpy(rpath, path, p - path +1); + else + errx(1, "%s: %s", path, strerror(ENAMETOOLONG)); + } + return rpath; +} + +const char* +_realpath(const char* path) +{ + static char rpath[PATH_MAX]; + int len; + char opath[PATH_MAX]; + char cwd[PATH_MAX]; + char *p; + + if ('/' == path[0] || '\0' == path[0]) + return path; + + bzero(rpath, sizeof(rpath)); + strlcpy(opath, path, PATH_MAX); + + if (getcwd(cwd, sizeof(cwd) -1) == NULL) + err(1, "getcwd"); + +re_realpath: + if ((p = rindex(opath, '/')) == NULL) { + if ('.' == path[0] && + ('\0' == path[1] || ('.' == path[1] && '\0' == path[2]))) + return realpath(path, rpath); + len = strlen(cwd) + strlen(path) +1; + if (PATH_MAX <= len) + errx(1, "%s: %s", path, strerror(ENAMETOOLONG)); + strlcpy(rpath, cwd, PATH_MAX); + strlcat(rpath, "/", PATH_MAX); + strlcat(rpath, path, PATH_MAX); + return rpath; + } + + if ('\0' == p[1]) { + *p = '\0'; + goto re_realpath; + } + + *p = '\0'; + len = strlen(cwd) + strlen(opath) +1; + if (PATH_MAX <= len) + errx(1, "%s: %s", path, strerror(ENAMETOOLONG)); + strlcat(cwd, "/", PATH_MAX); + strlcat(cwd, opath, PATH_MAX); + + if (!realpath(cwd, rpath)) + err(1, rpath); + + p++; + len = strlen(rpath) + strlen(p) + 1; + if (PATH_MAX <= len) + errx(1, "%s: %s", path, strerror(ENAMETOOLONG)); + strlcat(rpath, "/", PATH_MAX); + strlcat(rpath, p, PATH_MAX); + + return rpath; +} + void usage(void) {