xref: /freebsd/sys/fs/nfsclient/nfs_clcomsubs.c (revision 830940567b49bb0c08dfaed40418999e76616909)
1 /*-
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 /*
38  * These functions support the macros and help fiddle mbuf chains for
39  * the nfs op functions. They do things like create the rpc header and
40  * copy data between mbuf chains and uio lists.
41  */
42 #ifndef APPLEKEXT
43 #include <fs/nfs/nfsport.h>
44 
45 extern struct nfsstats newnfsstats;
46 extern struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS];
47 extern int ncl_mbuf_mlen;
48 extern enum vtype newnv2tov_type[8];
49 extern enum vtype nv34tov_type[8];
50 NFSCLSTATEMUTEX;
51 #endif	/* !APPLEKEXT */
52 
53 static nfsuint64 nfs_nullcookie = {{ 0, 0 }};
54 static struct {
55 	int	op;
56 	int	opcnt;
57 	const u_char *tag;
58 	int	taglen;
59 } nfsv4_opmap[NFS_NPROCS] = {
60 	{ 0, 1, "Null", 4 },
61 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
62 	{ NFSV4OP_SETATTR, 2, "Setattr", 7, },
63 	{ NFSV4OP_LOOKUP, 3, "Lookup", 6, },
64 	{ NFSV4OP_ACCESS, 2, "Access", 6, },
65 	{ NFSV4OP_READLINK, 2, "Readlink", 8, },
66 	{ NFSV4OP_READ, 1, "Read", 4, },
67 	{ NFSV4OP_WRITE, 2, "Write", 5, },
68 	{ NFSV4OP_OPEN, 3, "Open", 4, },
69 	{ NFSV4OP_CREATE, 3, "Create", 6, },
70 	{ NFSV4OP_CREATE, 1, "Create", 6, },
71 	{ NFSV4OP_CREATE, 3, "Create", 6, },
72 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
73 	{ NFSV4OP_REMOVE, 1, "Remove", 6, },
74 	{ NFSV4OP_SAVEFH, 5, "Rename", 6, },
75 	{ NFSV4OP_SAVEFH, 4, "Link", 4, },
76 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
77 	{ NFSV4OP_READDIR, 2, "Readdir", 7, },
78 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
79 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
80 	{ NFSV4OP_GETATTR, 1, "Getattr", 7, },
81 	{ NFSV4OP_COMMIT, 2, "Commit", 6, },
82 	{ NFSV4OP_LOOKUPP, 3, "Lookupp", 7, },
83 	{ NFSV4OP_SETCLIENTID, 1, "SetClientID", 11, },
84 	{ NFSV4OP_SETCLIENTIDCFRM, 1, "SetClientIDConfirm", 18, },
85 	{ NFSV4OP_LOCK, 1, "Lock", 4, },
86 	{ NFSV4OP_LOCKU, 1, "LockU", 5, },
87 	{ NFSV4OP_OPEN, 2, "Open", 4, },
88 	{ NFSV4OP_CLOSE, 1, "Close", 5, },
89 	{ NFSV4OP_OPENCONFIRM, 1, "Openconfirm", 11, },
90 	{ NFSV4OP_LOCKT, 1, "LockT", 5, },
91 	{ NFSV4OP_OPENDOWNGRADE, 1, "Opendowngrade", 13, },
92 	{ NFSV4OP_RENEW, 1, "Renew", 5, },
93 	{ NFSV4OP_PUTROOTFH, 1, "Dirpath", 7, },
94 	{ NFSV4OP_RELEASELCKOWN, 1, "Rellckown", 9, },
95 	{ NFSV4OP_DELEGRETURN, 1, "Delegret", 8, },
96 	{ NFSV4OP_DELEGRETURN, 3, "DelegRemove", 11, },
97 	{ NFSV4OP_DELEGRETURN, 7, "DelegRename1", 12, },
98 	{ NFSV4OP_DELEGRETURN, 9, "DelegRename2", 12, },
99 	{ NFSV4OP_GETATTR, 1, "Getacl", 6, },
100 	{ NFSV4OP_SETATTR, 1, "Setacl", 6, },
101 };
102 
103 
104 /*
105  * NFS RPCS that have large request message size.
106  */
107 static int nfs_bigrequest[NFS_NPROCS] = {
108 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 };
111 
112 /*
113  * Start building a request. Mostly just put the first file handle in
114  * place.
115  */
116 APPLESTATIC void
117 nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
118     u_int8_t *nfhp, int fhlen, u_int32_t **opcntpp)
119 {
120 	struct mbuf *mb;
121 	u_int32_t *tl;
122 	int opcnt;
123 	nfsattrbit_t attrbits;
124 
125 	/*
126 	 * First, fill in some of the fields of nd.
127 	 */
128 	if (NFSHASNFSV4(nmp))
129 		nd->nd_flag = ND_NFSV4;
130 	else if (NFSHASNFSV3(nmp))
131 		nd->nd_flag = ND_NFSV3;
132 	else
133 		nd->nd_flag = ND_NFSV2;
134 	nd->nd_procnum = procnum;
135 	nd->nd_repstat = 0;
136 
137 	/*
138 	 * Get the first mbuf for the request.
139 	 */
140 	if (nfs_bigrequest[procnum])
141 		NFSMCLGET(mb, M_WAIT);
142 	else
143 		NFSMGET(mb);
144 	mbuf_setlen(mb, 0);
145 	nd->nd_mreq = nd->nd_mb = mb;
146 	nd->nd_bpos = NFSMTOD(mb, caddr_t);
147 
148 	/*
149 	 * And fill the first file handle into the request.
150 	 */
151 	if (nd->nd_flag & ND_NFSV4) {
152 		opcnt = nfsv4_opmap[procnum].opcnt +
153 		    nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh;
154 		/*
155 		 * What should the tag really be?
156 		 */
157 		(void) nfsm_strtom(nd, nfsv4_opmap[procnum].tag,
158 			nfsv4_opmap[procnum].taglen);
159 		NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
160 		*tl++ = txdr_unsigned(NFSV4_MINORVERSION);
161 		if (opcntpp != NULL)
162 			*opcntpp = tl;
163 		*tl++ = txdr_unsigned(opcnt);
164 		if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh > 0) {
165 			*tl = txdr_unsigned(NFSV4OP_PUTFH);
166 			(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
167 			if (nfsv4_opflag[nfsv4_opmap[procnum].op].needscfh==2){
168 				NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
169 				*tl = txdr_unsigned(NFSV4OP_GETATTR);
170 				NFSWCCATTR_ATTRBIT(&attrbits);
171 				(void) nfsrv_putattrbit(nd, &attrbits);
172 				nd->nd_flag |= ND_V4WCCATTR;
173 			}
174 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
175 		}
176 		*tl = txdr_unsigned(nfsv4_opmap[procnum].op);
177 	} else {
178 		(void) nfsm_fhtom(nd, nfhp, fhlen, 0);
179 	}
180 	NFSINCRGLOBAL(newnfsstats.rpccnt[procnum]);
181 }
182 
183 #ifndef APPLE
184 /*
185  * copies a uio scatter/gather list to an mbuf chain.
186  * NOTE: can ony handle iovcnt == 1
187  */
188 APPLESTATIC void
189 nfsm_uiombuf(struct nfsrv_descript *nd, struct uio *uiop, int siz)
190 {
191 	char *uiocp;
192 	struct mbuf *mp, *mp2;
193 	int xfer, left, mlen;
194 	int uiosiz, clflg, rem;
195 	char *cp, *tcp;
196 
197 #ifdef DIAGNOSTIC
198 	if (uiop->uio_iovcnt != 1)
199 		panic("nfsm_uiotombuf: iovcnt != 1");
200 #endif
201 
202 	if (siz > ncl_mbuf_mlen)	/* or should it >= MCLBYTES ?? */
203 		clflg = 1;
204 	else
205 		clflg = 0;
206 	rem = NFSM_RNDUP(siz) - siz;
207 	mp = mp2 = nd->nd_mb;
208 	while (siz > 0) {
209 		left = uiop->uio_iov->iov_len;
210 		uiocp = uiop->uio_iov->iov_base;
211 		if (left > siz)
212 			left = siz;
213 		uiosiz = left;
214 		while (left > 0) {
215 			mlen = M_TRAILINGSPACE(mp);
216 			if (mlen == 0) {
217 				if (clflg)
218 					NFSMCLGET(mp, M_WAIT);
219 				else
220 					NFSMGET(mp);
221 				mbuf_setlen(mp, 0);
222 				mbuf_setnext(mp2, mp);
223 				mp2 = mp;
224 				mlen = M_TRAILINGSPACE(mp);
225 			}
226 			xfer = (left > mlen) ? mlen : left;
227 #ifdef notdef
228 			/* Not Yet.. */
229 			if (uiop->uio_iov->iov_op != NULL)
230 				(*(uiop->uio_iov->iov_op))
231 				(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
232 				    xfer);
233 			else
234 #endif
235 			if (uiop->uio_segflg == UIO_SYSSPACE)
236 			    NFSBCOPY(uiocp, NFSMTOD(mp, caddr_t) + mbuf_len(mp),
237 				xfer);
238 			else
239 			    copyin(CAST_USER_ADDR_T(uiocp), NFSMTOD(mp, caddr_t)
240 				+ mbuf_len(mp), xfer);
241 			mbuf_setlen(mp, mbuf_len(mp) + xfer);
242 			left -= xfer;
243 			uiocp += xfer;
244 			uiop->uio_offset += xfer;
245 			uiop->uio_resid -= xfer;
246 		}
247 		tcp = (char *)uiop->uio_iov->iov_base;
248 		tcp += uiosiz;
249 		uiop->uio_iov->iov_base = (void *)tcp;
250 		uiop->uio_iov->iov_len -= uiosiz;
251 		siz -= uiosiz;
252 	}
253 	if (rem > 0) {
254 		if (rem > M_TRAILINGSPACE(mp)) {
255 			NFSMGET(mp);
256 			mbuf_setlen(mp, 0);
257 			mbuf_setnext(mp2, mp);
258 		}
259 		cp = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
260 		for (left = 0; left < rem; left++)
261 			*cp++ = '\0';
262 		mbuf_setlen(mp, mbuf_len(mp) + rem);
263 		nd->nd_bpos = cp;
264 	} else
265 		nd->nd_bpos = NFSMTOD(mp, caddr_t) + mbuf_len(mp);
266 	nd->nd_mb = mp;
267 }
268 #endif	/* !APPLE */
269 
270 /*
271  * Load vnode attributes from the xdr file attributes.
272  * Returns EBADRPC if they can't be parsed, 0 otherwise.
273  */
274 APPLESTATIC int
275 nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
276 {
277 	struct nfs_fattr *fp;
278 	int error = 0;
279 
280 	if (nd->nd_flag & ND_NFSV4) {
281 		error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
282 		    NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
283 	} else if (nd->nd_flag & ND_NFSV3) {
284 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
285 		nap->na_type = nfsv34tov_type(fp->fa_type);
286 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
287 		nap->na_rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1),
288 			fxdr_unsigned(u_char, fp->fa3_rdev.specdata2));
289 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
290 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
291 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
292 		nap->na_size = fxdr_hyper(&fp->fa3_size);
293 		nap->na_blocksize = NFS_FABLKSIZE;
294 		nap->na_bytes = fxdr_hyper(&fp->fa3_used);
295 		nap->na_fileid = fxdr_unsigned(int32_t,
296 		    fp->fa3_fileid.nfsuquad[1]);
297 		fxdr_nfsv3time(&fp->fa3_atime, &nap->na_atime);
298 		fxdr_nfsv3time(&fp->fa3_ctime, &nap->na_ctime);
299 		fxdr_nfsv3time(&fp->fa3_mtime, &nap->na_mtime);
300 		nap->na_flags = 0;
301 		nap->na_filerev = 0;
302 	} else {
303 		NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V2FATTR);
304 		nap->na_type = nfsv2tov_type(fp->fa_type);
305 		nap->na_mode = fxdr_unsigned(u_short, fp->fa_mode);
306 		if (nap->na_type == VNON || nap->na_type == VREG)
307 			nap->na_type = IFTOVT(nap->na_mode);
308 		nap->na_rdev = fxdr_unsigned(dev_t, fp->fa2_rdev);
309 
310 		/*
311 		 * Really ugly NFSv2 kludge.
312 		 */
313 		if (nap->na_type == VCHR && nap->na_rdev == ((dev_t)-1))
314 			nap->na_type = VFIFO;
315 		nap->na_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
316 		nap->na_uid = fxdr_unsigned(uid_t, fp->fa_uid);
317 		nap->na_gid = fxdr_unsigned(gid_t, fp->fa_gid);
318 		nap->na_size = fxdr_unsigned(u_int32_t, fp->fa2_size);
319 		nap->na_blocksize = fxdr_unsigned(int32_t, fp->fa2_blocksize);
320 		nap->na_bytes =
321 		    (u_quad_t)fxdr_unsigned(int32_t, fp->fa2_blocks) *
322 		    NFS_FABLKSIZE;
323 		nap->na_fileid = fxdr_unsigned(int32_t, fp->fa2_fileid);
324 		fxdr_nfsv2time(&fp->fa2_atime, &nap->na_atime);
325 		fxdr_nfsv2time(&fp->fa2_mtime, &nap->na_mtime);
326 		nap->na_flags = 0;
327 		nap->na_ctime.tv_sec = fxdr_unsigned(u_int32_t,
328 		    fp->fa2_ctime.nfsv2_sec);
329 		nap->na_ctime.tv_nsec = 0;
330 		nap->na_gen = fxdr_unsigned(u_int32_t,fp->fa2_ctime.nfsv2_usec);
331 		nap->na_filerev = 0;
332 	}
333 nfsmout:
334 	return (error);
335 }
336 
337 /*
338  * This function finds the directory cookie that corresponds to the
339  * logical byte offset given.
340  */
341 APPLESTATIC nfsuint64 *
342 nfscl_getcookie(struct nfsnode *np, off_t off, int add)
343 {
344 	struct nfsdmap *dp, *dp2;
345 	int pos;
346 
347 	pos = off / NFS_DIRBLKSIZ;
348 	if (pos == 0) {
349 #ifdef DIAGNOSTIC
350 		if (add)
351 			panic("nfs getcookie add at 0");
352 #endif
353 		return (&nfs_nullcookie);
354 	}
355 	pos--;
356 	dp = LIST_FIRST(&np->n_cookies);
357 	if (!dp) {
358 		if (add) {
359 			MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap),
360 				M_NFSDIROFF, M_WAITOK);
361 			dp->ndm_eocookie = 0;
362 			LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list);
363 		} else
364 			return (NULL);
365 	}
366 	while (pos >= NFSNUMCOOKIES) {
367 		pos -= NFSNUMCOOKIES;
368 		if (LIST_NEXT(dp, ndm_list) != NULL) {
369 			if (!add && dp->ndm_eocookie < NFSNUMCOOKIES &&
370 				pos >= dp->ndm_eocookie)
371 				return (NULL);
372 			dp = LIST_NEXT(dp, ndm_list);
373 		} else if (add) {
374 			MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap),
375 				M_NFSDIROFF, M_WAITOK);
376 			dp2->ndm_eocookie = 0;
377 			LIST_INSERT_AFTER(dp, dp2, ndm_list);
378 			dp = dp2;
379 		} else
380 			return (NULL);
381 	}
382 	if (pos >= dp->ndm_eocookie) {
383 		if (add)
384 			dp->ndm_eocookie = pos + 1;
385 		else
386 			return (NULL);
387 	}
388 	return (&dp->ndm_cookies[pos]);
389 }
390 
391 /*
392  * Gets a file handle out of an nfs reply sent to the client and returns
393  * the file handle and the file's attributes.
394  * For V4, it assumes that Getfh and Getattr Op's results are here.
395  */
396 APPLESTATIC int
397 nfscl_mtofh(struct nfsrv_descript *nd, struct nfsfh **nfhpp,
398     struct nfsvattr *nap, int *attrflagp)
399 {
400 	u_int32_t *tl;
401 	int error = 0, flag = 1;
402 
403 	*nfhpp = NULL;
404 	*attrflagp = 0;
405 	/*
406 	 * First get the file handle and vnode.
407 	 */
408 	if (nd->nd_flag & ND_NFSV3) {
409 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
410 		flag = fxdr_unsigned(int, *tl);
411 	} else if (nd->nd_flag & ND_NFSV4) {
412 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
413 	}
414 	if (flag) {
415 		error = nfsm_getfh(nd, nfhpp);
416 		if (error)
417 			return (error);
418 	}
419 
420 	/*
421 	 * Now, get the attributes.
422 	 */
423 	if (nd->nd_flag & ND_NFSV4) {
424 		NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
425 	} else if (nd->nd_flag & ND_NFSV3) {
426 		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
427 		if (flag) {
428 			flag = fxdr_unsigned(int, *tl);
429 		} else if (fxdr_unsigned(int, *tl)) {
430 			error = nfsm_advance(nd, NFSX_V3FATTR, -1);
431 			if (error)
432 				return (error);
433 		}
434 	}
435 	if (flag) {
436 		error = nfsm_loadattr(nd, nap);
437 		if (!error)
438 			*attrflagp = 1;
439 	}
440 nfsmout:
441 	return (error);
442 }
443 
444 /*
445  * Put a state Id in the mbuf list.
446  */
447 APPLESTATIC void
448 nfsm_stateidtom(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, int flag)
449 {
450 	nfsv4stateid_t *st;
451 
452 	NFSM_BUILD(st, nfsv4stateid_t *, NFSX_STATEID);
453 	if (flag == NFSSTATEID_PUTALLZERO) {
454 		st->seqid = 0;
455 		st->other[0] = 0;
456 		st->other[1] = 0;
457 		st->other[2] = 0;
458 	} else if (flag == NFSSTATEID_PUTALLONE) {
459 		st->seqid = 0xffffffff;
460 		st->other[0] = 0xffffffff;
461 		st->other[1] = 0xffffffff;
462 		st->other[2] = 0xffffffff;
463 	} else {
464 		st->seqid = stateidp->seqid;
465 		st->other[0] = stateidp->other[0];
466 		st->other[1] = stateidp->other[1];
467 		st->other[2] = stateidp->other[2];
468 	}
469 }
470 
471 /*
472  * Initialize the owner/delegation sleep lock.
473  */
474 APPLESTATIC void
475 nfscl_lockinit(struct nfsv4lock *lckp)
476 {
477 
478 	lckp->nfslock_usecnt = 0;
479 	lckp->nfslock_lock = 0;
480 }
481 
482 /*
483  * Get an exclusive lock. (Not needed for OpenBSD4, since there is only one
484  * thread for each posix process in the kernel.)
485  */
486 APPLESTATIC void
487 nfscl_lockexcl(struct nfsv4lock *lckp, void *mutex)
488 {
489 	int igotlock;
490 
491 	do {
492 		igotlock = nfsv4_lock(lckp, 1, NULL, mutex);
493 	} while (!igotlock);
494 }
495 
496 /*
497  * Release an exclusive lock.
498  */
499 APPLESTATIC void
500 nfscl_lockunlock(struct nfsv4lock *lckp)
501 {
502 
503 	nfsv4_unlock(lckp, 0);
504 }
505 
506 /*
507  * Called to derefernce a lock on a stateid (delegation or open owner).
508  */
509 APPLESTATIC void
510 nfscl_lockderef(struct nfsv4lock *lckp)
511 {
512 
513 	NFSLOCKCLSTATE();
514 	lckp->nfslock_usecnt--;
515 	if (lckp->nfslock_usecnt == 0 && (lckp->nfslock_lock & NFSV4LOCK_WANTED)) {
516 		lckp->nfslock_lock &= ~NFSV4LOCK_WANTED;
517 		wakeup((caddr_t)lckp);
518 	}
519 	NFSUNLOCKCLSTATE();
520 }
521 
522