/* unlink.c: by ERS. This routine is in the public domain */ #include #include #include #include #include #include #include #include #include #include "symdir.h" #include "lib.h" #define error(x) ((errno = (x)), -1) __EXTERN char *strdup __PROTO((const char *s)); /* remove provided for ansi compatibility */ #ifndef __GNUC__ int remove(filename) const char *filename; { return unlink(filename); } #endif static struct delete { char *fname; struct delete *next; } *D = (struct delete *)NULL; static void do_deletes() { struct delete *d; d = D; while (d) { if(d->fname) { (void)Fdelete(d->fname); free(d->fname); } d = d->next; } } #ifdef __GNUC__ asm(".stabs \"_remove\",5,0,0,_unlink"); /* dept of clean tricks */ #endif int unlink(filename) const char * filename; { char name[FILENAME_MAX]; char *s; int r, fd; struct delete *d; int nameinfo; SYMDIR *dir = 0; SYMENTRY *ent = 0, *old; /* * _unx2dos returns _NM_LINK if "filename" was a symbolic link, * in which case __link_path[] is the path to the directory containing * the link, and __link_name[] is the name of the link. */ nameinfo = _unx2dos(filename, name); if (nameinfo == _NM_LINK) { /* a symbolic link */ if (!(dir = _read_symdir(__link_path))) return -1; old = 0; ent = dir->s_dir; while (ent) { if (!strcmp(ent->linkname, __link_name)) break; old = ent; ent = ent->next; } } else if (_lOK && nameinfo == _NM_CHANGE) { /* if symlinks are active, insist that the correct file name be given */ errno = ENOENT; return -1; } /* * unlink the real file, if no symbolic link was found (ent == 0) * or if the link was an automatic link. Unix allows open files * to be unlinked; TOS does not, and to get around this we check * the names of all the open files; if the one we want is found, * we arrange to unlink it on exit. */ if (!ent || (ent->flags & SD_AUTO)) { for (fd = 0; fd < __NHANDLES; fd++) { if ((s = __open_stat[fd].filename) && !strcmp(s, name)) { /* D == 0 means the atexit routine was not installed yet */ if (!D && atexit(do_deletes)) { _free_symdir(dir); return error(ENOMEM); } d = (struct delete *)malloc(sizeof(struct delete)); if (!d) { _free_symdir(dir); return error(ENOMEM); } /* hook the new "delete" structure into the chain */ d->fname = strdup(name); d->next = D; D = d; if(!(d->fname)) { /* note we link it in and then check, as we cannot undo the atexit(), (nor do we want to because there maybe others on the chain already). in do_deletes we'll just skip the null name entries */ _free_symdir(dir); return error(ENOMEM); } /* if there was an automatic link, delete it now */ if (ent) goto unlink_sym; return 0; } } r = (int)Fdelete(name); } /* * watch out for delete errors; if we have an auto link to a write * protected file, preserve the link; but if the auto link points * to a non-existent file, delete it */ if ( ent && r == -EACCESS) { _free_symdir(dir); return error(-r); } /* * having gotten this far, all that's left is to unlink the symbolic * link itself. "old" was set to the previous directory entry; we * remove "ent" from the chain, then rewrite the directory. */ unlink_sym: if (ent) { if (old) old->next = ent->next; else dir->s_dir = ent->next; r = _write_symdir(__link_path,dir); _free_symdir(dir); } if (r < 0) { return error(-r); } return 0; }