The c_link() function

The c_link() function is responsible for soft and hard links. A hard link is the “original” link from the dawn of history. It's a method that allows one resource (be it a directory or a file, depending on the support) to have multiple names. In the example in the symlink redirection, we created a symlink from resume.htm to ../resume.html; we could just as easily have created a hard link:

# ln ../resume.html resume.htm
Figure 1. A hard link implemented as two different attributes structures pointing to the same file.

In this case, both ../resume.html and resume.htm would be considered identical; there's no concept of “original” and “link” as there is with symlinks.

When the client calls link() or symlink() (or uses the command-line command ln), our RAM-disk resource manager's c_link() function will be called.

The c_link() function follows a similar code path as all of the other connect functions we've discussed so far (c_open() and c_readlink()), so once again we'll just focus on the differences:

int
cfs_c_link (resmgr_context_t *ctp, io_link_t *msg,
            RESMGR_HANDLE_T *handle, io_link_extra_t *extra)
{
  RESMGR_OCB_T  *ocb;
  des_t         parent, target;
  int           sts, sts2;
  char          *p, *s;
  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 (target.attr) {
    return (EEXIST);
  }

  // 1) find out what type of link we are creating
  switch (msg -> connect.extra_type) {
  // process a hard link
  case  _IO_CONNECT_EXTRA_LINK:
    ocb = extra -> ocb;
    p = strdup (target.name);
    if (p == NULL) {
      return (ENOMEM);
    }
    // 2) add a new directory entry
    if (sts = add_new_dirent (parent.attr, ocb -> attr, p)) {
      free (p);
      return (sts);
    }
    // 3) bump the link count
    ocb -> attr -> attr.nlink++;
    return (EOK);

  // process a symbolic link
  case  _IO_CONNECT_EXTRA_SYMLINK:
    p = target.name;
    s = strdup (extra -> path);
    if (s == NULL) {
      return (ENOMEM);
    }
    // 4) create a symlink entry
    target.attr = cfs_a_mksymlink (parent.attr, p, NULL);
    if (!target.attr) {
      free (s);
      return (errno);
    }
    // 5) write data
    target.attr -> type.symlinkdata = s;
    target.attr -> attr.nbytes = strlen (s);
    return (EOK);

  default:
    return (ENOSYS);
  }

  return (_RESMGR_DEFAULT);
}

The following is the code walkthrough for creating hard or symbolic links:

  1. The extra_type member of the connect message tells us what kind of link we're creating.
  2. For a hard link, we create a new directory entry. The utility function add_new_dirent() is responsible for adjusting the dirblocks member of the attributes structure to hold another entry, performing whatever allocation is needed (except for allocating the name, which is done with the strdup()). Notice that we get an OCB as part of the extra parameter passed to us. This OCB's extended attributes structure is the resource that we're creating the hard link to (yes, this means that our c_open() would have been called before this—that's done automatically).
  3. Since this is a hard link, we need to increment the link count of the object itself. Recall that we talked about named objects (the dirblocks array) and unnamed objects (the fileblocks member). The unnamed object is the actual entity that we bump the link count of, not the individual named objects.
  4. If we're creating a symlink, call the utility function cfs_a_mksymlink(), which allocates a directory entry within the parent. Notice that in the symlink case, we don't get an OCB, but rather a pathname as part of the extra parameter.
  5. Write the data into the extended attribute's symlinkdata member, and set the size to the length of the symlink content.