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: