+ BlueSkyInode *inode = lookup_fh(req, &argp->file);
+ if (inode == NULL) {
+ result.status = NFS3ERR_STALE;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ async_rpc_send_reply(req, &result);
+ return;
+ }
+ g_mutex_lock(inode->lock);
+
+ BlueSkyInode *dir = lookup_fh(req, &argp->link.dir);
+ if (dir == NULL) {
+ result.status = NFS3ERR_STALE;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ g_mutex_unlock(inode->lock);
+ async_rpc_send_reply(req, &result);
+ return;
+ }
+ g_mutex_lock(dir->lock);
+
+ encode_pre_wcc(&wcc, dir);
+ if (dir->type != BLUESKY_DIRECTORY) {
+ result.status = NFS3ERR_NOTDIR;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ g_mutex_unlock(inode->lock);
+ g_mutex_unlock(dir->lock);
+ async_rpc_send_reply(req, &result);
+ return;
+ }
+
+ if (!validate_filename(argp->link.name)
+ || strcmp(argp->link.name, ".") == 0
+ || strcmp(argp->link.name, "..") == 0
+ || bluesky_directory_lookup(dir, argp->link.name) != 0)
+ {
+ result.status = NFS3ERR_EXIST;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ g_mutex_unlock(inode->lock);
+ g_mutex_unlock(dir->lock);
+ async_rpc_send_reply(req, &result);
+ return;
+ }
+
+ if (!bluesky_directory_insert(dir, argp->link.name, inode->inum)) {
+ result.status = NFS3ERR_EXIST;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ g_mutex_unlock(inode->lock);
+ g_mutex_unlock(dir->lock);
+ async_rpc_send_reply(req, &result);
+ return;
+ }
+ inode->nlink++;
+ bluesky_inode_update_ctime(inode, FALSE);
+ bluesky_inode_update_ctime(dir, TRUE);