To unlink an entry, the following code is used:
int c_unlink (resmgr_context_t *ctp, io_unlink_t *msg, RESMGR_HANDLE_T *handle, void *reserved) { des_t parent, target; int sts, sts2; struct _client_info *cinfo; if ((sts = iofunc_client_info_ext (ctp, 0, &cinfo, IOFUNC_CLIENTINFO_GETGROUPS)) != EOK) { return (sts); } sts2 = connect_msg_to_attr (ctp, &msg -> connect, handle, &parent, &target, &sts, cinfo); (void)iofunc_client_info_ext_free (&cinfo); if (sts2 != EOK) { return (sts); } if (sts != EOK) { return (sts); } // see below if (target.attr == handle) { return (EBUSY); } return (cfs_rmnod (&parent, target.name, target.attr)); }
The code implementing c_unlink() is straightforward as well—we get the client information and resolve the pathname. The destination had better exist, so if we don't get an EOK we return the error to the client. Also, it's a really bad idea (read: bug) to unlink the mount point, so we make a special check against the target attribute's being equal to the mount point attribute, and return EBUSY if that's the case. Note that QNX 4 returns the constant EBUSY, QNX Neutrino returns EPERM, and OpenBSD returns EISDIR. So, there are plenty of constants to choose from in the real world! I like EBUSY.
Other than that, the actual work is done in cfs_rmnod(), below.
int cfs_rmnod (des_t *parent, char *name, cfs_attr_t *attr) { int sts; int i; // 1) remove target attr -> attr.nlink--; if ((sts = release_attr (attr)) != EOK) { return (sts); } // 2) remove the directory entry out of the parent for (i = 0; i < parent -> attr -> nels; i++) { // 3) skip empty directory entries if (parent -> attr -> type.dirblocks [i].name == NULL) { continue; } if (!strcmp (parent -> attr -> type.dirblocks [i].name, name)) { break; } } if (i == parent -> attr -> nels) { // huh. gone. This is either some kind of internal error, // or a race condition. return (ENOENT); } // 4) reclaim the space, and zero out the entry free (parent -> attr -> type.dirblocks [i].name); parent -> attr -> type.dirblocks [i].name = NULL; // 5) catch shrinkage at the tail end of the dirblocks[] while (parent -> attr -> type.dirblocks [parent -> attr -> nels - 1].name == NULL) { parent -> attr -> nels--; } // 6) could check the open count and do other reclamation // magic here, but we don't *have to* for now... return (EOK); }
Notice that we may not necessarily reclaim the space occupied by the resource! That's because the file could be in use by someone else. So the only time that it's appropriate to actually remove it is when the link count goes to zero, and that's checked for in the release_attr() routine as well as in the io_close_ocb() handler (below).
Here's the walkthrough: