housel@ea.ecn.purdue.edu (Peter S. Housel) (03/16/88)
Two weeks ago, I accidentally mv'd the contents of my home directory into a subdirectory of a subdirectory of my home directory. The effect of this was to make about 2/3 of the 10 Meg worth of files on my hard drive inaccessible. It took about six or seven hours of intensive hacking to get most of these files back, but many will have to be rewritten from scratch. The patch below keeps this from happening, by checking to make sure that the destination of a directory move is not in one of the source's subdirectories. Also, the code allready checked to make sure you couldn't rename ".." or ".", but going through the back door (i.e. "./." or "dir/..") was still possible. One more thing: it would really be nice if fsck could handle putting unref'ed inodes into a "lost+found" directory. -Peter S. Housel- housel@ei.ecn.purdue.edu ...!pur-ee!housel Apply the following patch (to the 1.2 version of mv.c) using fix. Be sure to "chown root mv; chmod 4755 mv". (Note- the patch is only about 40 bytes shorter than the entire mv.c) ---------------------------cut here--------------------------------------- 3a4 > * 3/15/88 - P. Housel More directory bug fixes 9c10,11 < int error = 0; --- > extern char *rindex(); > 49d50 < if (error) exit (1); 59,61c60,70 < < /* It's too dangerous to fool with "." or ".." ! */ < if((strcmp(old, ".") == 0) || (strcmp(old, "..") == 0)) { --- > char parent[64]; > char *oldbase; > int i; > > if((oldbase = rindex(old, '/')) == 0) > oldbase = old; > else > ++oldbase; > > /* It's too dangerous to fool with "." or ".." ! */ > if((strcmp(oldbase, ".") == 0) || (strcmp(oldbase, "..") == 0)) { 70,87c79,126 < if (!stat (new, &st)) < if((st.st_mode & S_IFMT) != S_IFDIR) < unlink (new); < else { < char *p, *rindex(); < < if ((strlen(old) + strlen(new) + 2) > 64) { < cant(old); < error++; < return; < } < strcpy(name, new); < strcat(name, "/"); < p = rindex(old, '/'); < strcat(name, p ? p : old); < new = name; < } < stat (old, &st); --- > if (!stat (new, &st1)) > if((st1.st_mode & S_IFMT) != S_IFDIR) > unlink (new); > else { > if ((strlen(oldbase) + strlen(new) + 2) > 64) { > cant(old); > } > strcpy(name, new); > strcat(name, "/"); > strcat(name, oldbase); > new = name; > } > > strcpy(parent, new); > > for(i = (strlen(parent) - 1); i > 0; i--) { > if(parent[i] == '/') break; > } > > if(i == 0) { > strcpy(parent, "."); > } > else { > /* null-terminate name at last slash */ > parent[i] = '\0'; > } > > /* prevent moving a directory into its own subdirectory */ > if((st.st_mode & S_IFMT) == S_IFDIR) { > char lower[128]; > short int prevdev = -1; > unsigned short previno; > > strcpy(lower, parent); > while(1) { > if(stat(lower, &st1) || (st1.st_dev == st.st_dev > && st1.st_ino == st.st_ino)) > cant(old); > > /* stop at root */ > if(st1.st_dev == prevdev && st1.st_ino == previno) > break; > prevdev = st1.st_dev; > previno = st1.st_ino; > strcat(lower, "/.."); > } > } > 112,161c151,175 < int i; < char parent[64], dotdot[64]; < < strcpy(parent, new); < < /* Determine the name for the parent of < ** the new name by counting back until we < ** hit a '/' or the begining of the string < */ < < for(i = (strlen(parent) - 1); i > 0; i--) { < if(parent[i] == '/') break; < } < < /* If there are no slashes, then the name is < ** in the current directory, so its parent < ** is ".". Otherwise, the parent is the name < ** up to the last slash. < */ < if(i == 0) { < strcpy(parent, "."); < } < else { < /* null-terminate name at last slash */ < parent[i] = '\0'; < } < < /* Unlink the ".." entry */ < strcpy(dotdot, new); < strcat(dotdot, "/.."); < unlink(dotdot); < < /* Now link it to its parent */ < link(parent, dotdot); < } < < utime (new, &st.st_atime); < unlink(old); < } < < cant(name) < char *name; < { < std_err("mv: can't move "); < std_err (name); < std_err ("\n"); < exit (1); < } < < --- > char dotdot[64]; > > /* Unlink the ".." entry */ > strcpy(dotdot, new); > strcat(dotdot, "/.."); > unlink(dotdot); > > /* Now link it to its parent */ > link(parent, dotdot); > } > > utime (new, &st.st_atime); > unlink(old); > } > > cant(name) > char *name; > { > std_err("mv: can't move "); > std_err (name); > std_err ("\n"); > exit (1); > } > >