NFSv4: Update nfs_open() to support symlinks
Signed-off-by: Ronnie Sahlberg <ronniesahlberg@gmail.com>libnfs-4.0.0-vitalif
parent
9f44b157f0
commit
5a78bd5a7a
242
lib/nfs_v4.c
242
lib/nfs_v4.c
|
@ -130,7 +130,7 @@ struct nfs4_cb_data {
|
|||
/* Do not follow symlinks for the final component on a lookup.
|
||||
* I.e. stat vs lstat
|
||||
*/
|
||||
#define LOOKUP_FLAG_FINAL_NO_FOLLOW 0x0001
|
||||
#define LOOKUP_FLAG_NO_FOLLOW 0x0001
|
||||
int flags;
|
||||
|
||||
/* Application callback and data */
|
||||
|
@ -569,6 +569,10 @@ nfs4_lookup_path_2_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_open_readlink(struct rpc_context *rpc, COMPOUND4res *res,
|
||||
struct nfs4_cb_data *data);
|
||||
|
||||
static void
|
||||
nfs4_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
|
||||
void *private_data)
|
||||
|
@ -578,7 +582,7 @@ nfs4_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
COMPOUND4args args;
|
||||
nfs_argop4 *op;
|
||||
COMPOUND4res *res = command_data;
|
||||
unsigned int i;
|
||||
int i;
|
||||
int resolve_link = 0;
|
||||
char *path, *tmp;
|
||||
|
||||
|
@ -614,7 +618,7 @@ nfs4_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < res->resarray.resarray_len; i++) {
|
||||
for (i = 0; i < (int)res->resarray.resarray_len; i++) {
|
||||
if (res->resarray.resarray_val[i].resop == OP_GETATTR) {
|
||||
GETATTR4resok *garesok;
|
||||
struct nfs_stat_64 st;
|
||||
|
@ -638,9 +642,24 @@ nfs4_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
}
|
||||
}
|
||||
|
||||
if (data->flags & LOOKUP_FLAG_FINAL_NO_FOLLOW) {
|
||||
/* Open/create is special since the final component for the file
|
||||
* object is sent as part of the OP_OPEN command. So even if the
|
||||
* directory path is all good and resolved, we still need to check
|
||||
* the attributes for the final component and resolve it if it too
|
||||
* is a symlink.
|
||||
*/
|
||||
if (!resolve_link) {
|
||||
if (nfs4_open_readlink(rpc, res, data) < 0) {
|
||||
/* It was a symlink and we have started trying to
|
||||
* resolve it. Nothing more to do here.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->flags & LOOKUP_FLAG_NO_FOLLOW) {
|
||||
/* Do not resolve the final component of the path
|
||||
* even if it is a symlink.
|
||||
* if it is a symlink.
|
||||
*/
|
||||
resolve_link = 0;
|
||||
}
|
||||
|
@ -655,7 +674,7 @@ nfs4_lookup_path_1_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
|
||||
/* Find the lookup that failed and the associated fh */
|
||||
data->link.idx = 0;
|
||||
for (i = 0; i < res->resarray.resarray_len; i++) {
|
||||
for (i = 0; i < (int)res->resarray.resarray_len; i++) {
|
||||
if (res->resarray.resarray_val[i].resop == OP_LOOKUP) {
|
||||
if (res->resarray.resarray_val[i].nfs_resop4_u.oplookup.status == NFS4ERR_SYMLINK) {
|
||||
break;
|
||||
|
@ -1110,7 +1129,7 @@ nfs4_stat64_async(struct nfs_context *nfs, const char *path,
|
|||
data->cb = cb;
|
||||
data->private_data = private_data;
|
||||
if (no_follow) {
|
||||
data->flags |= LOOKUP_FLAG_FINAL_NO_FOLLOW;
|
||||
data->flags |= LOOKUP_FLAG_NO_FOLLOW;
|
||||
}
|
||||
data->path = nfs4_resolve_path(nfs, path);
|
||||
if (data->path == NULL) {
|
||||
|
@ -1338,53 +1357,6 @@ nfs4_rmdir_async(struct nfs_context *nfs, const char *orig_path,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* filler.flags are the open flags
|
||||
* filler.data is the object name
|
||||
*/
|
||||
static void
|
||||
nfs4_populate_open(struct nfs4_cb_data *data, nfs_argop4 *op)
|
||||
{
|
||||
struct nfs_context *nfs = data->nfs;
|
||||
ACCESS4args *aargs;
|
||||
OPEN4args *oargs;
|
||||
|
||||
/* Access */
|
||||
op[0].argop = OP_ACCESS;
|
||||
aargs = &op[0].nfs_argop4_u.opaccess;
|
||||
memset(aargs, 0, sizeof(*aargs));
|
||||
|
||||
if (data->filler.flags & O_WRONLY) {
|
||||
aargs->access |= ACCESS4_MODIFY;
|
||||
}
|
||||
if (data->filler.flags & O_RDWR) {
|
||||
aargs->access |= ACCESS4_READ|ACCESS4_MODIFY;
|
||||
}
|
||||
if (!(data->filler.flags & (O_WRONLY|O_RDWR))) {
|
||||
aargs->access |= ACCESS4_READ;
|
||||
}
|
||||
|
||||
/* Open */
|
||||
op[1].argop = OP_OPEN;
|
||||
oargs = &op[1].nfs_argop4_u.opopen;
|
||||
memset(oargs, 0, sizeof(*oargs));
|
||||
|
||||
oargs->seqid = nfs->seqid++;
|
||||
oargs->share_access = OPEN4_SHARE_ACCESS_READ;
|
||||
oargs->share_deny = OPEN4_SHARE_DENY_NONE;
|
||||
oargs->owner.clientid = nfs->clientid;
|
||||
oargs->owner.owner.owner_len = strlen(nfs->client_name);
|
||||
oargs->owner.owner.owner_val = nfs->client_name;
|
||||
oargs->openhow.opentype = OPEN4_NOCREATE;
|
||||
oargs->claim.claim = CLAIM_NULL;
|
||||
oargs->claim.open_claim4_u.file.utf8string_len =
|
||||
strlen(data->filler.data);
|
||||
oargs->claim.open_claim4_u.file.utf8string_val =
|
||||
data->filler.data;
|
||||
|
||||
/* GetFH */
|
||||
op[2].argop = OP_GETFH;
|
||||
}
|
||||
|
||||
static void
|
||||
nfs4_open_confirm_cb(struct rpc_context *rpc, int status, void *command_data,
|
||||
void *private_data)
|
||||
|
@ -1529,6 +1501,166 @@ nfs4_open_cb(struct rpc_context *rpc, int status, void *command_data,
|
|||
free_nfs4_cb_data(data);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs4_open_readlink_cb(struct rpc_context *rpc, int status, void *command_data,
|
||||
void *private_data)
|
||||
{
|
||||
struct nfs4_cb_data *data = private_data;
|
||||
struct nfs_context *nfs = data->nfs;
|
||||
COMPOUND4res *res = command_data;
|
||||
READLINK4resok *rlresok;
|
||||
int i;
|
||||
char *path;
|
||||
|
||||
assert(rpc->magic == RPC_CONTEXT_MAGIC);
|
||||
|
||||
if (check_nfs4_error(nfs, status, data, res, "READLINK")) {
|
||||
free_nfs4_cb_data(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((i = nfs4_find_op(nfs, data, res, OP_READLINK, "READLINK")) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
rlresok = &res->resarray.resarray_val[i].nfs_resop4_u.opreadlink.READLINK4res_u.resok4;
|
||||
|
||||
path = malloc(2 + strlen(data->path) +
|
||||
strlen(rlresok->link.utf8string_val));
|
||||
if (path == NULL) {
|
||||
nfs_set_error(nfs, "Out of memory. Failed to allocate "
|
||||
"path");
|
||||
data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
|
||||
data->private_data);
|
||||
free_nfs4_cb_data(data);
|
||||
return;
|
||||
}
|
||||
sprintf(path, "%s/%s", data->path, rlresok->link.utf8string_val);
|
||||
|
||||
/* We have resolved the final component and created a new path.
|
||||
* Try to call open again.
|
||||
*/
|
||||
if (nfs4_open_async(nfs, path, data->filler.flags,
|
||||
data->cb, data->private_data) < 0) {
|
||||
data->cb(-ENOMEM, nfs, nfs_get_error(nfs), data->private_data);
|
||||
free_nfs4_cb_data(data);
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
free_nfs4_cb_data(data);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static void
|
||||
nfs4_populate_lookup_readlink(struct nfs4_cb_data *data, nfs_argop4 *op)
|
||||
{
|
||||
LOOKUP4args *largs;
|
||||
|
||||
op[0].argop = OP_LOOKUP;
|
||||
|
||||
largs = &op[0].nfs_argop4_u.oplookup;
|
||||
largs->objname.utf8string_len = strlen(data->filler.data);
|
||||
largs->objname.utf8string_val = data->filler.data;
|
||||
|
||||
op[1].argop = OP_READLINK;
|
||||
}
|
||||
|
||||
/* If the final component in the open was a symlink we need to resolve it and
|
||||
* re-try the nfs4_open_async()
|
||||
*/
|
||||
static int
|
||||
nfs4_open_readlink(struct rpc_context *rpc, COMPOUND4res *res,
|
||||
struct nfs4_cb_data *data)
|
||||
{
|
||||
struct nfs_context *nfs = data->nfs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)res->resarray.resarray_len; i++) {
|
||||
OPEN4res *ores;
|
||||
|
||||
if (res->resarray.resarray_val[i].resop != OP_OPEN) {
|
||||
continue;
|
||||
}
|
||||
ores = &res->resarray.resarray_val[i].nfs_resop4_u.opopen;
|
||||
|
||||
if (ores->status != NFS4ERR_SYMLINK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data->filler.flags & O_NOFOLLOW) {
|
||||
nfs_set_error(nfs, "Symlink encountered during "
|
||||
"open(O_NOFOLLOW)");
|
||||
data->cb(-ELOOP, nfs, nfs_get_error(nfs),
|
||||
data->private_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The object we need to do readlink on is already stored in
|
||||
* data->filler.data so *populate* can just grab it from there.
|
||||
*/
|
||||
data->filler.func = nfs4_populate_lookup_readlink;
|
||||
data->filler.num_op = 2;
|
||||
|
||||
if (nfs4_lookup_path_async(nfs, data,
|
||||
nfs4_open_readlink_cb) < 0) {
|
||||
data->cb(-ENOMEM, nfs, nfs_get_error(nfs),
|
||||
data->private_data);
|
||||
free_nfs4_cb_data(data);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* filler.flags are the open flags
|
||||
* filler.data is the object name
|
||||
*/
|
||||
static void
|
||||
nfs4_populate_open(struct nfs4_cb_data *data, nfs_argop4 *op)
|
||||
{
|
||||
struct nfs_context *nfs = data->nfs;
|
||||
ACCESS4args *aargs;
|
||||
OPEN4args *oargs;
|
||||
|
||||
/* Access */
|
||||
op[0].argop = OP_ACCESS;
|
||||
aargs = &op[0].nfs_argop4_u.opaccess;
|
||||
memset(aargs, 0, sizeof(*aargs));
|
||||
|
||||
if (data->filler.flags & O_WRONLY) {
|
||||
aargs->access |= ACCESS4_MODIFY;
|
||||
}
|
||||
if (data->filler.flags & O_RDWR) {
|
||||
aargs->access |= ACCESS4_READ|ACCESS4_MODIFY;
|
||||
}
|
||||
if (!(data->filler.flags & (O_WRONLY|O_RDWR))) {
|
||||
aargs->access |= ACCESS4_READ;
|
||||
}
|
||||
|
||||
/* Open */
|
||||
op[1].argop = OP_OPEN;
|
||||
oargs = &op[1].nfs_argop4_u.opopen;
|
||||
memset(oargs, 0, sizeof(*oargs));
|
||||
|
||||
oargs->seqid = nfs->seqid++;
|
||||
oargs->share_access = OPEN4_SHARE_ACCESS_READ;
|
||||
oargs->share_deny = OPEN4_SHARE_DENY_NONE;
|
||||
oargs->owner.clientid = nfs->clientid;
|
||||
oargs->owner.owner.owner_len = strlen(nfs->client_name);
|
||||
oargs->owner.owner.owner_val = nfs->client_name;
|
||||
oargs->openhow.opentype = OPEN4_NOCREATE;
|
||||
oargs->claim.claim = CLAIM_NULL;
|
||||
oargs->claim.open_claim4_u.file.utf8string_len =
|
||||
strlen(data->filler.data);
|
||||
oargs->claim.open_claim4_u.file.utf8string_val =
|
||||
data->filler.data;
|
||||
|
||||
/* GetFH */
|
||||
op[2].argop = OP_GETFH;
|
||||
}
|
||||
|
||||
int
|
||||
nfs4_open_async(struct nfs_context *nfs, const char *orig_path, int flags,
|
||||
nfs_cb cb, void *private_data)
|
||||
|
@ -1957,7 +2089,7 @@ nfs4_readlink_async(struct nfs_context *nfs, const char *path, nfs_cb cb,
|
|||
data->cb = cb;
|
||||
data->private_data = private_data;
|
||||
data->path = nfs4_resolve_path(nfs, path);
|
||||
data->flags |= LOOKUP_FLAG_FINAL_NO_FOLLOW;
|
||||
data->flags |= LOOKUP_FLAG_NO_FOLLOW;
|
||||
|
||||
if (data->path == NULL) {
|
||||
nfs_set_error(nfs, "Out of memory duplicating path");
|
||||
|
|
|
@ -51,11 +51,7 @@ echo -n "Open a symlinked file (rel) (9) ... "
|
|||
./prog_open_read "${TESTURL}/?version=${VERS}" "." subdir4/open8 O_RDONLY >/dev/null || failure
|
||||
success
|
||||
|
||||
echo -n "Open a symlinked path with O_NOFOLLOW (rel) (10) ... "
|
||||
./prog_open_read "${TESTURL}/?version=${VERS}" "." subdir4/open3 O_RDONLY,O_NOFOLLOW 2>/dev/null && failure
|
||||
success
|
||||
|
||||
echo -n "Open a symlinked file with O_NOFOLLOW (rel) (11) ... "
|
||||
echo -n "Open a symlinked file with O_NOFOLLOW (rel) (10) ... "
|
||||
./prog_open_read "${TESTURL}/?version=${VERS}" "." subdir/open8 O_RDONLY,O_NOFOLLOW 2>/dev/null && failure
|
||||
success
|
||||
|
||||
|
|
Loading…
Reference in New Issue