Updated: October 28, 2024 |
The functionality to perform a rename can be done in one of two ways. You can simply return ENOSYS, which tells the client's rename() that you don't support renaming, or you can handle it. If you do return ENOSYS, an end user might not notice it right away, because the command-line utility mv deals with that and copies the file to the new location and then deletes the original. For a RAM disk, with small files, the time it takes to do the copy and unlink is imperceptible. However, simply changing the name of a directory that has lots of large files will take a long time, even though all you're doing is changing the name of the directory!
In order to properly implement rename functionality, there are two interesting issues:
The rename logic is further complicated by the fact that we are dealing with two paths instead of just one. In the c_link() case, one of the pathnames was implied by either an OCB (hard link) or actually given (symlink)—for the symlink we viewed the second pathname as a text string, without doing any particular checking on it.
You'll notice this two path impact when we look at the code:
int cfs_c_rename (resmgr_context_t *ctp, io_rename_t *msg, RESMGR_HANDLE_T *handle, io_rename_extra_t *extra) { // source and destination parents and targets des_t sparent, starget, dparent, dtarget; des_t components [_POSIX_PATH_MAX]; int ncomponents; int sts; char *p; int i; struct _client_info *cinfo; // 1) check for "initial subset" (mv x x/a) case i = strlen (extra -> path); if (!strncmp (extra -> path, msg -> connect.path, i)) { // source could be a subset, check character after // end of subset in destination if (msg -> connect.path [i] == 0 || msg -> connect.path [i] == '/') { // source is identical to destination, or is a subset return (EINVAL); } } // get client info if ((sts = iofunc_client_info_ext (ctp, 0, &cinfo, IOFUNC_CLIENTINFO_GETGROUPS)) != EOK) { return (sts); } // 2) do destination resolution first in case we need to // do a redirect or otherwise fail the request. if (connect_msg_to_attr (ctp, &msg -> connect, handle, &dparent, &dtarget, &sts, cinfo)) { (void)iofunc_client_info_ext_free (&cinfo); return (sts); } // 3) if the destination exists, kill it and continue. if (sts != ENOENT) { if (sts == EOK) { if ((sts = cfs_rmnod (&dparent, dtarget.name, dtarget.attr)) != EOK) { (void)iofunc_client_info_ext_free (&cinfo); return (sts); } } else { (void)iofunc_client_info_ext_free (&cinfo); return (sts); } } // 4) use our friend pathwalk() for source resolution. ncomponents = _POSIX_PATH_MAX; sts = pathwalk (ctp, extra -> path, handle, 0, components, &ncomponents, cinfo); (void)iofunc_client_info_ext_free (&cinfo); // 5) missing directory component if (sts == ENOTDIR) { return (sts); } // 6) missing non-final component if (components [ncomponents].name != NULL && sts == ENOENT) { return (sts); } // 7) an annoying bug if (ncomponents < 2) { // can't move the root directory of the filesystem return (EBUSY); } starget = components [ncomponents - 1]; sparent = components [ncomponents - 2]; p = strdup (dtarget.name); if (p == NULL) { return (ENOMEM); } // 8) create new... if ((sts = add_new_dirent (dparent.attr, starget.attr, p)) != EOK) { free (p); return (sts); } starget.attr -> attr.nlink++; // 9) delete old return (cfs_rmnod (&sparent, starget.name, starget.attr)); }
The walkthrough is as follows: