1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * linux/fs/lockd/svc4proc.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Lockd server procedures. We don't implement the NLM_*_RES 61da177e4SLinus Torvalds * procedures because we don't use the async procedures. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de> 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/types.h> 121da177e4SLinus Torvalds #include <linux/time.h> 131da177e4SLinus Torvalds #include <linux/lockd/lockd.h> 141da177e4SLinus Torvalds #include <linux/lockd/share.h> 155ccb0066SStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #define NLMDBG_FACILITY NLMDBG_CLIENT 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds /* 201da177e4SLinus Torvalds * Obtain client and file from arguments 211da177e4SLinus Torvalds */ 2252921e02SAl Viro static __be32 231da177e4SLinus Torvalds nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp, 241da177e4SLinus Torvalds struct nlm_host **hostp, struct nlm_file **filp) 251da177e4SLinus Torvalds { 261da177e4SLinus Torvalds struct nlm_host *host = NULL; 271da177e4SLinus Torvalds struct nlm_file *file = NULL; 281da177e4SLinus Torvalds struct nlm_lock *lock = &argp->lock; 2952921e02SAl Viro __be32 error = 0; 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds /* nfsd callbacks must have been installed for this procedure */ 321da177e4SLinus Torvalds if (!nlmsvc_ops) 331da177e4SLinus Torvalds return nlm_lck_denied_nolocks; 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* Obtain host handle */ 36db4e4c9aSOlaf Kirch if (!(host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len)) 37977faf39SOlaf Kirch || (argp->monitor && nsm_monitor(host) < 0)) 381da177e4SLinus Torvalds goto no_locks; 391da177e4SLinus Torvalds *hostp = host; 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds /* Obtain file pointer. Not used by FREE_ALL call. */ 421da177e4SLinus Torvalds if (filp != NULL) { 43*7f024fcdSJ. Bruce Fields int mode = lock_to_openmode(&lock->fl); 44*7f024fcdSJ. Bruce Fields 452dc6f19eSJ. Bruce Fields error = nlm_lookup_file(rqstp, &file, lock); 462dc6f19eSJ. Bruce Fields if (error) 471da177e4SLinus Torvalds goto no_locks; 481da177e4SLinus Torvalds *filp = file; 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds /* Set up the missing parts of the file_lock structure */ 51*7f024fcdSJ. Bruce Fields lock->fl.fl_file = file->f_file[mode]; 52646d73e9SBenjamin Coddington lock->fl.fl_pid = current->tgid; 531da177e4SLinus Torvalds lock->fl.fl_lmops = &nlmsvc_lock_operations; 5489e0edfbSBenjamin Coddington nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid); 5589e0edfbSBenjamin Coddington if (!lock->fl.fl_owner) { 5689e0edfbSBenjamin Coddington /* lockowner allocation has failed */ 5789e0edfbSBenjamin Coddington nlmsvc_release_host(host); 5889e0edfbSBenjamin Coddington return nlm_lck_denied_nolocks; 5989e0edfbSBenjamin Coddington } 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds return 0; 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds no_locks: 6567216b94SChuck Lever nlmsvc_release_host(host); 661da177e4SLinus Torvalds if (error) 671da177e4SLinus Torvalds return error; 681da177e4SLinus Torvalds return nlm_lck_denied_nolocks; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* 721da177e4SLinus Torvalds * NULL: Test for presence of service 731da177e4SLinus Torvalds */ 747111c66eSAl Viro static __be32 75a6beb732SChristoph Hellwig nlm4svc_proc_null(struct svc_rqst *rqstp) 761da177e4SLinus Torvalds { 771da177e4SLinus Torvalds dprintk("lockd: NULL called\n"); 781da177e4SLinus Torvalds return rpc_success; 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds /* 821da177e4SLinus Torvalds * TEST: Check for conflicting lock 831da177e4SLinus Torvalds */ 847111c66eSAl Viro static __be32 85a6beb732SChristoph Hellwig __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp) 861da177e4SLinus Torvalds { 87a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 881da177e4SLinus Torvalds struct nlm_host *host; 891da177e4SLinus Torvalds struct nlm_file *file; 90317602f3SHarvey Harrison __be32 rc = rpc_success; 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds dprintk("lockd: TEST4 called\n"); 931da177e4SLinus Torvalds resp->cookie = argp->cookie; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds /* Obtain client and file */ 961da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 97d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds /* Now check for conflicting locks */ 1008f920d5eSJeff Layton resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); 1015ea0d750SMarc Eshel if (resp->status == nlm_drop_reply) 102b7e6b869SOleg Drokin rc = rpc_drop_reply; 103b7e6b869SOleg Drokin else 1041da177e4SLinus Torvalds dprintk("lockd: TEST4 status %d\n", ntohl(resp->status)); 105b7e6b869SOleg Drokin 10689e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 10767216b94SChuck Lever nlmsvc_release_host(host); 1081da177e4SLinus Torvalds nlm_release_file(file); 109b7e6b869SOleg Drokin return rc; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds 1127111c66eSAl Viro static __be32 113a6beb732SChristoph Hellwig nlm4svc_proc_test(struct svc_rqst *rqstp) 1141da177e4SLinus Torvalds { 115a6beb732SChristoph Hellwig return __nlm4svc_proc_test(rqstp, rqstp->rq_resp); 116a6beb732SChristoph Hellwig } 117a6beb732SChristoph Hellwig 118a6beb732SChristoph Hellwig static __be32 119a6beb732SChristoph Hellwig __nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp) 120a6beb732SChristoph Hellwig { 121a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 1221da177e4SLinus Torvalds struct nlm_host *host; 1231da177e4SLinus Torvalds struct nlm_file *file; 124317602f3SHarvey Harrison __be32 rc = rpc_success; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds dprintk("lockd: LOCK called\n"); 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds resp->cookie = argp->cookie; 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds /* Obtain client and file */ 1311da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 132d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds #if 0 1351da177e4SLinus Torvalds /* If supplied state doesn't match current state, we assume it's 1361da177e4SLinus Torvalds * an old request that time-warped somehow. Any error return would 1371da177e4SLinus Torvalds * do in this case because it's irrelevant anyway. 1381da177e4SLinus Torvalds * 1391da177e4SLinus Torvalds * NB: We don't retrieve the remote host's state yet. 1401da177e4SLinus Torvalds */ 1411da177e4SLinus Torvalds if (host->h_nsmstate && host->h_nsmstate != argp->state) { 1421da177e4SLinus Torvalds resp->status = nlm_lck_denied_nolocks; 1431da177e4SLinus Torvalds } else 1441da177e4SLinus Torvalds #endif 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds /* Now try to lock the file */ 1476cde4de8SJeff Layton resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, 148b2b50289SJ. Bruce Fields argp->block, &argp->cookie, 149b2b50289SJ. Bruce Fields argp->reclaim); 1501a8322b2SMarc Eshel if (resp->status == nlm_drop_reply) 151b7e6b869SOleg Drokin rc = rpc_drop_reply; 152b7e6b869SOleg Drokin else 1531da177e4SLinus Torvalds dprintk("lockd: LOCK status %d\n", ntohl(resp->status)); 154b7e6b869SOleg Drokin 15589e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 15667216b94SChuck Lever nlmsvc_release_host(host); 1571da177e4SLinus Torvalds nlm_release_file(file); 158b7e6b869SOleg Drokin return rc; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1617111c66eSAl Viro static __be32 162a6beb732SChristoph Hellwig nlm4svc_proc_lock(struct svc_rqst *rqstp) 1631da177e4SLinus Torvalds { 164a6beb732SChristoph Hellwig return __nlm4svc_proc_lock(rqstp, rqstp->rq_resp); 165a6beb732SChristoph Hellwig } 166a6beb732SChristoph Hellwig 167a6beb732SChristoph Hellwig static __be32 168a6beb732SChristoph Hellwig __nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_res *resp) 169a6beb732SChristoph Hellwig { 170a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 1711da177e4SLinus Torvalds struct nlm_host *host; 1721da177e4SLinus Torvalds struct nlm_file *file; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds dprintk("lockd: CANCEL called\n"); 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds resp->cookie = argp->cookie; 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* Don't accept requests during grace period */ 1795ccb0066SStanislav Kinsbursky if (locks_in_grace(SVC_NET(rqstp))) { 1801da177e4SLinus Torvalds resp->status = nlm_lck_denied_grace_period; 1811da177e4SLinus Torvalds return rpc_success; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 1841da177e4SLinus Torvalds /* Obtain client and file */ 1851da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 186d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds /* Try to cancel request. */ 1895ccb0066SStanislav Kinsbursky resp->status = nlmsvc_cancel_blocked(SVC_NET(rqstp), file, &argp->lock); 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds dprintk("lockd: CANCEL status %d\n", ntohl(resp->status)); 19289e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 19367216b94SChuck Lever nlmsvc_release_host(host); 1941da177e4SLinus Torvalds nlm_release_file(file); 1951da177e4SLinus Torvalds return rpc_success; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 198a6beb732SChristoph Hellwig static __be32 199a6beb732SChristoph Hellwig nlm4svc_proc_cancel(struct svc_rqst *rqstp) 200a6beb732SChristoph Hellwig { 201a6beb732SChristoph Hellwig return __nlm4svc_proc_cancel(rqstp, rqstp->rq_resp); 202a6beb732SChristoph Hellwig } 203a6beb732SChristoph Hellwig 2041da177e4SLinus Torvalds /* 2051da177e4SLinus Torvalds * UNLOCK: release a lock 2061da177e4SLinus Torvalds */ 2077111c66eSAl Viro static __be32 208a6beb732SChristoph Hellwig __nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_res *resp) 2091da177e4SLinus Torvalds { 210a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 2111da177e4SLinus Torvalds struct nlm_host *host; 2121da177e4SLinus Torvalds struct nlm_file *file; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds dprintk("lockd: UNLOCK called\n"); 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds resp->cookie = argp->cookie; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds /* Don't accept new lock requests during grace period */ 2195ccb0066SStanislav Kinsbursky if (locks_in_grace(SVC_NET(rqstp))) { 2201da177e4SLinus Torvalds resp->status = nlm_lck_denied_grace_period; 2211da177e4SLinus Torvalds return rpc_success; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds /* Obtain client and file */ 2251da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 226d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* Now try to remove the lock */ 2295ccb0066SStanislav Kinsbursky resp->status = nlmsvc_unlock(SVC_NET(rqstp), file, &argp->lock); 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status)); 23289e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 23367216b94SChuck Lever nlmsvc_release_host(host); 2341da177e4SLinus Torvalds nlm_release_file(file); 2351da177e4SLinus Torvalds return rpc_success; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 238a6beb732SChristoph Hellwig static __be32 239a6beb732SChristoph Hellwig nlm4svc_proc_unlock(struct svc_rqst *rqstp) 240a6beb732SChristoph Hellwig { 241a6beb732SChristoph Hellwig return __nlm4svc_proc_unlock(rqstp, rqstp->rq_resp); 242a6beb732SChristoph Hellwig } 243a6beb732SChristoph Hellwig 2441da177e4SLinus Torvalds /* 2451da177e4SLinus Torvalds * GRANTED: A server calls us to tell that a process' lock request 2461da177e4SLinus Torvalds * was granted 2471da177e4SLinus Torvalds */ 2487111c66eSAl Viro static __be32 249a6beb732SChristoph Hellwig __nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_res *resp) 2501da177e4SLinus Torvalds { 251a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 252a6beb732SChristoph Hellwig 2531da177e4SLinus Torvalds resp->cookie = argp->cookie; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds dprintk("lockd: GRANTED called\n"); 256dcff09f1SChuck Lever resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); 2571da177e4SLinus Torvalds dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); 2581da177e4SLinus Torvalds return rpc_success; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 261a6beb732SChristoph Hellwig static __be32 262a6beb732SChristoph Hellwig nlm4svc_proc_granted(struct svc_rqst *rqstp) 263a6beb732SChristoph Hellwig { 264a6beb732SChristoph Hellwig return __nlm4svc_proc_granted(rqstp, rqstp->rq_resp); 265a6beb732SChristoph Hellwig } 266a6beb732SChristoph Hellwig 2671da177e4SLinus Torvalds /* 268d4716624STrond Myklebust * This is the generic lockd callback for async RPC calls 269d4716624STrond Myklebust */ 270d4716624STrond Myklebust static void nlm4svc_callback_exit(struct rpc_task *task, void *data) 271d4716624STrond Myklebust { 272c041b5ffSChuck Lever dprintk("lockd: %5u callback returned %d\n", task->tk_pid, 273d4716624STrond Myklebust -task->tk_status); 274d4716624STrond Myklebust } 275d4716624STrond Myklebust 276d4716624STrond Myklebust static void nlm4svc_callback_release(void *data) 277d4716624STrond Myklebust { 2787db836d4SChuck Lever nlmsvc_release_call(data); 279d4716624STrond Myklebust } 280d4716624STrond Myklebust 281d4716624STrond Myklebust static const struct rpc_call_ops nlm4svc_callback_ops = { 282d4716624STrond Myklebust .rpc_call_done = nlm4svc_callback_exit, 283d4716624STrond Myklebust .rpc_release = nlm4svc_callback_release, 284d4716624STrond Myklebust }; 285d4716624STrond Myklebust 286d4716624STrond Myklebust /* 2871da177e4SLinus Torvalds * `Async' versions of the above service routines. They aren't really, 2881da177e4SLinus Torvalds * because we send the callback before the reply proper. I hope this 2891da177e4SLinus Torvalds * doesn't break any clients. 2901da177e4SLinus Torvalds */ 291a6beb732SChristoph Hellwig static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, 292a6beb732SChristoph Hellwig __be32 (*func)(struct svc_rqst *, struct nlm_res *)) 293d4716624STrond Myklebust { 294a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 295d4716624STrond Myklebust struct nlm_host *host; 296d4716624STrond Myklebust struct nlm_rqst *call; 2977111c66eSAl Viro __be32 stat; 298d4716624STrond Myklebust 299db4e4c9aSOlaf Kirch host = nlmsvc_lookup_host(rqstp, 300db4e4c9aSOlaf Kirch argp->lock.caller, 301db4e4c9aSOlaf Kirch argp->lock.len); 302d4716624STrond Myklebust if (host == NULL) 303d4716624STrond Myklebust return rpc_system_err; 304d4716624STrond Myklebust 305d4716624STrond Myklebust call = nlm_alloc_call(host); 306446945abSAl Viro nlmsvc_release_host(host); 307d4716624STrond Myklebust if (call == NULL) 308d4716624STrond Myklebust return rpc_system_err; 309d4716624STrond Myklebust 310a6beb732SChristoph Hellwig stat = func(rqstp, &call->a_res); 311d4716624STrond Myklebust if (stat != 0) { 3127db836d4SChuck Lever nlmsvc_release_call(call); 313d4716624STrond Myklebust return stat; 314d4716624STrond Myklebust } 315d4716624STrond Myklebust 316d4716624STrond Myklebust call->a_flags = RPC_TASK_ASYNC; 317d4716624STrond Myklebust if (nlm_async_reply(call, proc, &nlm4svc_callback_ops) < 0) 318d4716624STrond Myklebust return rpc_system_err; 319d4716624STrond Myklebust return rpc_success; 320d4716624STrond Myklebust } 321d4716624STrond Myklebust 322a6beb732SChristoph Hellwig static __be32 nlm4svc_proc_test_msg(struct svc_rqst *rqstp) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds dprintk("lockd: TEST_MSG called\n"); 325a6beb732SChristoph Hellwig return nlm4svc_callback(rqstp, NLMPROC_TEST_RES, __nlm4svc_proc_test); 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds 328a6beb732SChristoph Hellwig static __be32 nlm4svc_proc_lock_msg(struct svc_rqst *rqstp) 3291da177e4SLinus Torvalds { 3301da177e4SLinus Torvalds dprintk("lockd: LOCK_MSG called\n"); 331a6beb732SChristoph Hellwig return nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, __nlm4svc_proc_lock); 3321da177e4SLinus Torvalds } 3331da177e4SLinus Torvalds 334a6beb732SChristoph Hellwig static __be32 nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp) 3351da177e4SLinus Torvalds { 3361da177e4SLinus Torvalds dprintk("lockd: CANCEL_MSG called\n"); 337a6beb732SChristoph Hellwig return nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, __nlm4svc_proc_cancel); 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 340a6beb732SChristoph Hellwig static __be32 nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds dprintk("lockd: UNLOCK_MSG called\n"); 343a6beb732SChristoph Hellwig return nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, __nlm4svc_proc_unlock); 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 346a6beb732SChristoph Hellwig static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds dprintk("lockd: GRANTED_MSG called\n"); 349a6beb732SChristoph Hellwig return nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, __nlm4svc_proc_granted); 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds /* 3531da177e4SLinus Torvalds * SHARE: create a DOS share or alter existing share. 3541da177e4SLinus Torvalds */ 3557111c66eSAl Viro static __be32 356a6beb732SChristoph Hellwig nlm4svc_proc_share(struct svc_rqst *rqstp) 3571da177e4SLinus Torvalds { 358a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 359a6beb732SChristoph Hellwig struct nlm_res *resp = rqstp->rq_resp; 3601da177e4SLinus Torvalds struct nlm_host *host; 3611da177e4SLinus Torvalds struct nlm_file *file; 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds dprintk("lockd: SHARE called\n"); 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds resp->cookie = argp->cookie; 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds /* Don't accept new lock requests during grace period */ 3685ccb0066SStanislav Kinsbursky if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) { 3691da177e4SLinus Torvalds resp->status = nlm_lck_denied_grace_period; 3701da177e4SLinus Torvalds return rpc_success; 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds /* Obtain client and file */ 3741da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 375d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds /* Now try to create the share */ 3781da177e4SLinus Torvalds resp->status = nlmsvc_share_file(host, file, argp); 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds dprintk("lockd: SHARE status %d\n", ntohl(resp->status)); 38189e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 38267216b94SChuck Lever nlmsvc_release_host(host); 3831da177e4SLinus Torvalds nlm_release_file(file); 3841da177e4SLinus Torvalds return rpc_success; 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds /* 3881da177e4SLinus Torvalds * UNSHARE: Release a DOS share. 3891da177e4SLinus Torvalds */ 3907111c66eSAl Viro static __be32 391a6beb732SChristoph Hellwig nlm4svc_proc_unshare(struct svc_rqst *rqstp) 3921da177e4SLinus Torvalds { 393a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 394a6beb732SChristoph Hellwig struct nlm_res *resp = rqstp->rq_resp; 3951da177e4SLinus Torvalds struct nlm_host *host; 3961da177e4SLinus Torvalds struct nlm_file *file; 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds dprintk("lockd: UNSHARE called\n"); 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds resp->cookie = argp->cookie; 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds /* Don't accept requests during grace period */ 4035ccb0066SStanislav Kinsbursky if (locks_in_grace(SVC_NET(rqstp))) { 4041da177e4SLinus Torvalds resp->status = nlm_lck_denied_grace_period; 4051da177e4SLinus Torvalds return rpc_success; 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds /* Obtain client and file */ 4091da177e4SLinus Torvalds if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) 410d343fce1SNeilBrown return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; 4111da177e4SLinus Torvalds 4121da177e4SLinus Torvalds /* Now try to lock the file */ 4131da177e4SLinus Torvalds resp->status = nlmsvc_unshare_file(host, file, argp); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status)); 41689e0edfbSBenjamin Coddington nlmsvc_release_lockowner(&argp->lock); 41767216b94SChuck Lever nlmsvc_release_host(host); 4181da177e4SLinus Torvalds nlm_release_file(file); 4191da177e4SLinus Torvalds return rpc_success; 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds /* 4231da177e4SLinus Torvalds * NM_LOCK: Create an unmonitored lock 4241da177e4SLinus Torvalds */ 4257111c66eSAl Viro static __be32 426a6beb732SChristoph Hellwig nlm4svc_proc_nm_lock(struct svc_rqst *rqstp) 4271da177e4SLinus Torvalds { 428a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 429a6beb732SChristoph Hellwig 4301da177e4SLinus Torvalds dprintk("lockd: NM_LOCK called\n"); 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds argp->monitor = 0; /* just clean the monitor flag */ 433a6beb732SChristoph Hellwig return nlm4svc_proc_lock(rqstp); 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds /* 4371da177e4SLinus Torvalds * FREE_ALL: Release all locks and shares held by client 4381da177e4SLinus Torvalds */ 4397111c66eSAl Viro static __be32 440a6beb732SChristoph Hellwig nlm4svc_proc_free_all(struct svc_rqst *rqstp) 4411da177e4SLinus Torvalds { 442a6beb732SChristoph Hellwig struct nlm_args *argp = rqstp->rq_argp; 4431da177e4SLinus Torvalds struct nlm_host *host; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds /* Obtain client */ 4461da177e4SLinus Torvalds if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL)) 4471da177e4SLinus Torvalds return rpc_success; 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds nlmsvc_free_host_resources(host); 45067216b94SChuck Lever nlmsvc_release_host(host); 4511da177e4SLinus Torvalds return rpc_success; 4521da177e4SLinus Torvalds } 4531da177e4SLinus Torvalds 4541da177e4SLinus Torvalds /* 4551da177e4SLinus Torvalds * SM_NOTIFY: private callback from statd (not part of official NLM proto) 4561da177e4SLinus Torvalds */ 4577111c66eSAl Viro static __be32 458a6beb732SChristoph Hellwig nlm4svc_proc_sm_notify(struct svc_rqst *rqstp) 4591da177e4SLinus Torvalds { 460a6beb732SChristoph Hellwig struct nlm_reboot *argp = rqstp->rq_argp; 461a6beb732SChristoph Hellwig 4621da177e4SLinus Torvalds dprintk("lockd: SM_NOTIFY called\n"); 463b85e4676SChuck Lever 464b85e4676SChuck Lever if (!nlm_privileged_requester(rqstp)) { 465ad06e4bdSChuck Lever char buf[RPC_MAX_ADDRBUFLEN]; 466ad06e4bdSChuck Lever printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", 467ad06e4bdSChuck Lever svc_print_addr(rqstp, buf, sizeof(buf))); 4681da177e4SLinus Torvalds return rpc_system_err; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4710ad95472SAndrey Ryabinin nlm_host_rebooted(SVC_NET(rqstp), argp); 4721da177e4SLinus Torvalds return rpc_success; 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds /* 4761da177e4SLinus Torvalds * client sent a GRANTED_RES, let's remove the associated block 4771da177e4SLinus Torvalds */ 4787111c66eSAl Viro static __be32 479a6beb732SChristoph Hellwig nlm4svc_proc_granted_res(struct svc_rqst *rqstp) 4801da177e4SLinus Torvalds { 481a6beb732SChristoph Hellwig struct nlm_res *argp = rqstp->rq_argp; 482a6beb732SChristoph Hellwig 4831da177e4SLinus Torvalds if (!nlmsvc_ops) 4841da177e4SLinus Torvalds return rpc_success; 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds dprintk("lockd: GRANTED_RES called\n"); 4871da177e4SLinus Torvalds 48839be4502SOlaf Kirch nlmsvc_grant_reply(&argp->cookie, argp->status); 4891da177e4SLinus Torvalds return rpc_success; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49249d99608SChuck Lever static __be32 49349d99608SChuck Lever nlm4svc_proc_unused(struct svc_rqst *rqstp) 49449d99608SChuck Lever { 49549d99608SChuck Lever return rpc_proc_unavail; 49649d99608SChuck Lever } 49749d99608SChuck Lever 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds /* 5001da177e4SLinus Torvalds * NLM Server procedures. 5011da177e4SLinus Torvalds */ 5021da177e4SLinus Torvalds 5031da177e4SLinus Torvalds struct nlm_void { int dummy; }; 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds #define Ck (1+XDR_QUADLEN(NLM_MAXCOOKIELEN)) /* cookie */ 5061da177e4SLinus Torvalds #define No (1+1024/4) /* netobj */ 5071da177e4SLinus Torvalds #define St 1 /* status */ 5081da177e4SLinus Torvalds #define Rg 4 /* range (offset + length) */ 5091da177e4SLinus Torvalds 51049d99608SChuck Lever const struct svc_procedure nlmsvc_procedures4[24] = { 51149d99608SChuck Lever [NLMPROC_NULL] = { 51249d99608SChuck Lever .pc_func = nlm4svc_proc_null, 51349d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 51449d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 51549d99608SChuck Lever .pc_argsize = sizeof(struct nlm_void), 51649d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 51749d99608SChuck Lever .pc_xdrressize = St, 5182289e87bSChuck Lever .pc_name = "NULL", 51949d99608SChuck Lever }, 52049d99608SChuck Lever [NLMPROC_TEST] = { 52149d99608SChuck Lever .pc_func = nlm4svc_proc_test, 52249d99608SChuck Lever .pc_decode = nlm4svc_decode_testargs, 52349d99608SChuck Lever .pc_encode = nlm4svc_encode_testres, 52449d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 52549d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 52649d99608SChuck Lever .pc_xdrressize = Ck+St+2+No+Rg, 5272289e87bSChuck Lever .pc_name = "TEST", 52849d99608SChuck Lever }, 52949d99608SChuck Lever [NLMPROC_LOCK] = { 53049d99608SChuck Lever .pc_func = nlm4svc_proc_lock, 53149d99608SChuck Lever .pc_decode = nlm4svc_decode_lockargs, 53249d99608SChuck Lever .pc_encode = nlm4svc_encode_res, 53349d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 53449d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 53549d99608SChuck Lever .pc_xdrressize = Ck+St, 5362289e87bSChuck Lever .pc_name = "LOCK", 53749d99608SChuck Lever }, 53849d99608SChuck Lever [NLMPROC_CANCEL] = { 53949d99608SChuck Lever .pc_func = nlm4svc_proc_cancel, 54049d99608SChuck Lever .pc_decode = nlm4svc_decode_cancargs, 54149d99608SChuck Lever .pc_encode = nlm4svc_encode_res, 54249d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 54349d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 54449d99608SChuck Lever .pc_xdrressize = Ck+St, 5452289e87bSChuck Lever .pc_name = "CANCEL", 54649d99608SChuck Lever }, 54749d99608SChuck Lever [NLMPROC_UNLOCK] = { 54849d99608SChuck Lever .pc_func = nlm4svc_proc_unlock, 54949d99608SChuck Lever .pc_decode = nlm4svc_decode_unlockargs, 55049d99608SChuck Lever .pc_encode = nlm4svc_encode_res, 55149d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 55249d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 55349d99608SChuck Lever .pc_xdrressize = Ck+St, 5542289e87bSChuck Lever .pc_name = "UNLOCK", 55549d99608SChuck Lever }, 55649d99608SChuck Lever [NLMPROC_GRANTED] = { 55749d99608SChuck Lever .pc_func = nlm4svc_proc_granted, 55849d99608SChuck Lever .pc_decode = nlm4svc_decode_testargs, 55949d99608SChuck Lever .pc_encode = nlm4svc_encode_res, 56049d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 56149d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 56249d99608SChuck Lever .pc_xdrressize = Ck+St, 5632289e87bSChuck Lever .pc_name = "GRANTED", 56449d99608SChuck Lever }, 56549d99608SChuck Lever [NLMPROC_TEST_MSG] = { 56649d99608SChuck Lever .pc_func = nlm4svc_proc_test_msg, 56749d99608SChuck Lever .pc_decode = nlm4svc_decode_testargs, 56849d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 56949d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 57049d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 57149d99608SChuck Lever .pc_xdrressize = St, 5722289e87bSChuck Lever .pc_name = "TEST_MSG", 57349d99608SChuck Lever }, 57449d99608SChuck Lever [NLMPROC_LOCK_MSG] = { 57549d99608SChuck Lever .pc_func = nlm4svc_proc_lock_msg, 57649d99608SChuck Lever .pc_decode = nlm4svc_decode_lockargs, 57749d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 57849d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 57949d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 58049d99608SChuck Lever .pc_xdrressize = St, 5812289e87bSChuck Lever .pc_name = "LOCK_MSG", 58249d99608SChuck Lever }, 58349d99608SChuck Lever [NLMPROC_CANCEL_MSG] = { 58449d99608SChuck Lever .pc_func = nlm4svc_proc_cancel_msg, 58549d99608SChuck Lever .pc_decode = nlm4svc_decode_cancargs, 58649d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 58749d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 58849d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 58949d99608SChuck Lever .pc_xdrressize = St, 5902289e87bSChuck Lever .pc_name = "CANCEL_MSG", 59149d99608SChuck Lever }, 59249d99608SChuck Lever [NLMPROC_UNLOCK_MSG] = { 59349d99608SChuck Lever .pc_func = nlm4svc_proc_unlock_msg, 59449d99608SChuck Lever .pc_decode = nlm4svc_decode_unlockargs, 59549d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 59649d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 59749d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 59849d99608SChuck Lever .pc_xdrressize = St, 5992289e87bSChuck Lever .pc_name = "UNLOCK_MSG", 60049d99608SChuck Lever }, 60149d99608SChuck Lever [NLMPROC_GRANTED_MSG] = { 60249d99608SChuck Lever .pc_func = nlm4svc_proc_granted_msg, 60349d99608SChuck Lever .pc_decode = nlm4svc_decode_testargs, 60449d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 60549d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 60649d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 60749d99608SChuck Lever .pc_xdrressize = St, 6082289e87bSChuck Lever .pc_name = "GRANTED_MSG", 60949d99608SChuck Lever }, 61049d99608SChuck Lever [NLMPROC_TEST_RES] = { 61149d99608SChuck Lever .pc_func = nlm4svc_proc_null, 61249d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 61349d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 61449d99608SChuck Lever .pc_argsize = sizeof(struct nlm_res), 61549d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 61649d99608SChuck Lever .pc_xdrressize = St, 6172289e87bSChuck Lever .pc_name = "TEST_RES", 61849d99608SChuck Lever }, 61949d99608SChuck Lever [NLMPROC_LOCK_RES] = { 62049d99608SChuck Lever .pc_func = nlm4svc_proc_null, 62149d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 62249d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 62349d99608SChuck Lever .pc_argsize = sizeof(struct nlm_res), 62449d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 62549d99608SChuck Lever .pc_xdrressize = St, 6262289e87bSChuck Lever .pc_name = "LOCK_RES", 62749d99608SChuck Lever }, 62849d99608SChuck Lever [NLMPROC_CANCEL_RES] = { 62949d99608SChuck Lever .pc_func = nlm4svc_proc_null, 63049d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 63149d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 63249d99608SChuck Lever .pc_argsize = sizeof(struct nlm_res), 63349d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 63449d99608SChuck Lever .pc_xdrressize = St, 6352289e87bSChuck Lever .pc_name = "CANCEL_RES", 63649d99608SChuck Lever }, 63749d99608SChuck Lever [NLMPROC_UNLOCK_RES] = { 63849d99608SChuck Lever .pc_func = nlm4svc_proc_null, 63949d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 64049d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 64149d99608SChuck Lever .pc_argsize = sizeof(struct nlm_res), 64249d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 64349d99608SChuck Lever .pc_xdrressize = St, 6442289e87bSChuck Lever .pc_name = "UNLOCK_RES", 64549d99608SChuck Lever }, 64649d99608SChuck Lever [NLMPROC_GRANTED_RES] = { 64749d99608SChuck Lever .pc_func = nlm4svc_proc_granted_res, 64849d99608SChuck Lever .pc_decode = nlm4svc_decode_res, 64949d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 65049d99608SChuck Lever .pc_argsize = sizeof(struct nlm_res), 65149d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 65249d99608SChuck Lever .pc_xdrressize = St, 6532289e87bSChuck Lever .pc_name = "GRANTED_RES", 65449d99608SChuck Lever }, 65549d99608SChuck Lever [NLMPROC_NSM_NOTIFY] = { 65649d99608SChuck Lever .pc_func = nlm4svc_proc_sm_notify, 65749d99608SChuck Lever .pc_decode = nlm4svc_decode_reboot, 65849d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 65949d99608SChuck Lever .pc_argsize = sizeof(struct nlm_reboot), 66049d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 66149d99608SChuck Lever .pc_xdrressize = St, 6622289e87bSChuck Lever .pc_name = "SM_NOTIFY", 66349d99608SChuck Lever }, 66449d99608SChuck Lever [17] = { 66549d99608SChuck Lever .pc_func = nlm4svc_proc_unused, 66649d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 66749d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 66849d99608SChuck Lever .pc_argsize = sizeof(struct nlm_void), 66949d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 67049d99608SChuck Lever .pc_xdrressize = 0, 6712289e87bSChuck Lever .pc_name = "UNUSED", 67249d99608SChuck Lever }, 67349d99608SChuck Lever [18] = { 67449d99608SChuck Lever .pc_func = nlm4svc_proc_unused, 67549d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 67649d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 67749d99608SChuck Lever .pc_argsize = sizeof(struct nlm_void), 67849d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 67949d99608SChuck Lever .pc_xdrressize = 0, 6802289e87bSChuck Lever .pc_name = "UNUSED", 68149d99608SChuck Lever }, 68249d99608SChuck Lever [19] = { 68349d99608SChuck Lever .pc_func = nlm4svc_proc_unused, 68449d99608SChuck Lever .pc_decode = nlm4svc_decode_void, 68549d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 68649d99608SChuck Lever .pc_argsize = sizeof(struct nlm_void), 68749d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 68849d99608SChuck Lever .pc_xdrressize = 0, 6892289e87bSChuck Lever .pc_name = "UNUSED", 69049d99608SChuck Lever }, 69149d99608SChuck Lever [NLMPROC_SHARE] = { 69249d99608SChuck Lever .pc_func = nlm4svc_proc_share, 69349d99608SChuck Lever .pc_decode = nlm4svc_decode_shareargs, 69449d99608SChuck Lever .pc_encode = nlm4svc_encode_shareres, 69549d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 69649d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 69749d99608SChuck Lever .pc_xdrressize = Ck+St+1, 6982289e87bSChuck Lever .pc_name = "SHARE", 69949d99608SChuck Lever }, 70049d99608SChuck Lever [NLMPROC_UNSHARE] = { 70149d99608SChuck Lever .pc_func = nlm4svc_proc_unshare, 70249d99608SChuck Lever .pc_decode = nlm4svc_decode_shareargs, 70349d99608SChuck Lever .pc_encode = nlm4svc_encode_shareres, 70449d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 70549d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 70649d99608SChuck Lever .pc_xdrressize = Ck+St+1, 7072289e87bSChuck Lever .pc_name = "UNSHARE", 70849d99608SChuck Lever }, 70949d99608SChuck Lever [NLMPROC_NM_LOCK] = { 71049d99608SChuck Lever .pc_func = nlm4svc_proc_nm_lock, 71149d99608SChuck Lever .pc_decode = nlm4svc_decode_lockargs, 71249d99608SChuck Lever .pc_encode = nlm4svc_encode_res, 71349d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 71449d99608SChuck Lever .pc_ressize = sizeof(struct nlm_res), 71549d99608SChuck Lever .pc_xdrressize = Ck+St, 7162289e87bSChuck Lever .pc_name = "NM_LOCK", 71749d99608SChuck Lever }, 71849d99608SChuck Lever [NLMPROC_FREE_ALL] = { 71949d99608SChuck Lever .pc_func = nlm4svc_proc_free_all, 72049d99608SChuck Lever .pc_decode = nlm4svc_decode_notify, 72149d99608SChuck Lever .pc_encode = nlm4svc_encode_void, 72249d99608SChuck Lever .pc_argsize = sizeof(struct nlm_args), 72349d99608SChuck Lever .pc_ressize = sizeof(struct nlm_void), 72449d99608SChuck Lever .pc_xdrressize = St, 7252289e87bSChuck Lever .pc_name = "FREE_ALL", 72649d99608SChuck Lever }, 7271da177e4SLinus Torvalds }; 728