nfsproc3_link_3_svc(link3args *argp, struct svc_req *rqstp)
{
static link3res result;
+ struct wcc_data wcc;
+ memset(&wcc, 0, sizeof(wcc));
- result.status = NFS3ERR_NOTSUPP;
+ BlueSkyInode *inode = lookup_fh(&argp->file);
+ if (inode == NULL) {
+ result.status = NFS3ERR_STALE;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ return &result;
+ }
+
+ BlueSkyInode *dir = lookup_fh(&argp->link.dir);
+ if (dir == NULL) {
+ result.status = NFS3ERR_STALE;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ return &result;
+ }
+
+ encode_pre_wcc(&wcc, dir);
+ if (dir->type != BLUESKY_DIRECTORY) {
+ result.status = NFS3ERR_NOTDIR;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ return &result;
+ }
+
+ 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;
+ return &result;
+ }
+
+ if (!bluesky_directory_insert(dir, argp->link.name, inode->inum)) {
+ result.status = NFS3ERR_EXIST;
+ result.link3res_u.res.linkdir_wcc = wcc;
+ return &result;
+ }
+ inode->nlink++;
+ bluesky_inode_update_ctime(inode, 0);
+
+ result.status = NFS3_OK;
+ wcc.after.present = TRUE;
+ encode_fattr3(&wcc.after.post_op_attr_u.attributes, dir);
+ result.link3res_u.res.file_attributes.present = TRUE;
+ encode_fattr3(&result.link3res_u.res.file_attributes.post_op_attr_u.attributes, inode);
+ result.link3res_u.res.linkdir_wcc = wcc;
return &result;
}