This naturally brings us to the io_close_ocb() function. In most resource managers, you'd let the default library function, iofunc_close_ocb_default(), do the work. However, in our case, we may need to free a resource. Consider the case where a client performs the following perfectly legal (and useful for things like temporary files) code:
fp = fopen ("/ramdisk/tmpfile", "r+"); unlink ("/ramdisk/tmpfile"); // do some processing with the file fclose (fp);
We cannot release the resources for the /ramdisk/tmpfile until after the link count (the number of open file descriptors to the file) goes to zero.
The fclose() will eventually translate within the C library into a close(), which will then trigger our RAM disk's io_close_ocb() handler. Only when the count goes to zero can we free the data.
Here's the code for the io_close_ocb():
int cfs_io_close_ocb (resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb) { cfs_attr_t *attr; int sts; attr = ocb -> attr; sts = iofunc_close_ocb (ctp, ocb, &attr -> attr); if (sts == EOK) { // release_attr makes sure that no one is using it... sts = release_attr (attr); } return (sts); }
Note the attr -> attr — the helper function iofunc_close_ocb() expects the normal, nonextended attributes structure.
Once again, we rely on the services of release_attr() to ensure that the link count is zero.
Here's the source for release_attr() (from attr.c):
int release_attr (cfs_attr_t *attr) { int i; // 1) check the count if (!attr -> attr.nlink && !attr -> attr.count) { // decide what kind (file or dir) this entry is... if (S_ISDIR (attr -> attr.mode)) { // 2) it's a directory, see if it's empty if (attr -> nels > 2) { return (ENOTEMPTY); } // 3) need to free "." and ".." free (attr -> type.dirblocks [0].name); free (attr -> type.dirblocks [0].attr); free (attr -> type.dirblocks [1].name); free (attr -> type.dirblocks [1].attr); // 4) release the dirblocks[] if (attr -> type.dirblocks) { free (attr -> type.dirblocks); free (attr); } } else if (S_ISREG (attr -> attr.mode)) { // 5) a regular file for (i = 0; i < attr -> nels; i++) { cfs_block_free (attr, attr -> type.fileblocks [i].iov_base); attr -> type.fileblocks [i].iov_base = NULL; } // 6) release the fileblocks[] if (attr -> type.fileblocks) { free (attr -> type.fileblocks); free (attr); } } else if (S_ISLNK (attr -> attr.mode)) { // 7) a symlink, delete the contents free (attr -> type.symlinkdata); free (attr); } } // 8) return EOK if everything went well return (EOK); }
Note that the definition of empty is slightly different for a directory. A directory is considered empty if it has just the two entries . and .. within it.
You'll also note that we call free() to release all the objects. It's important that all the objects be allocated (whether via malloc()/calloc() for the dirblocks and fileblocks, or via stdrup() for the symlinkdata).
The code walkthrough is as follows: