The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/ibcs2/ibcs2_misc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1995 Steven Wallace
    3  * Copyright (c) 1994, 1995 Scott Bartram
    4  * Copyright (c) 1992, 1993
    5  *      The Regents of the University of California.  All rights reserved.
    6  *
    7  * This software was developed by the Computer Systems Engineering group
    8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
    9  * contributed to Berkeley.
   10  *
   11  * All advertising materials mentioning features or use of this software
   12  * must display the following acknowledgement:
   13  *      This product includes software developed by the University of
   14  *      California, Lawrence Berkeley Laboratory.
   15  *
   16  * Redistribution and use in source and binary forms, with or without
   17  * modification, are permitted provided that the following conditions
   18  * are met:
   19  * 1. Redistributions of source code must retain the above copyright
   20  *    notice, this list of conditions and the following disclaimer.
   21  * 2. Redistributions in binary form must reproduce the above copyright
   22  *    notice, this list of conditions and the following disclaimer in the
   23  *    documentation and/or other materials provided with the distribution.
   24  * 3. All advertising materials mentioning features or use of this software
   25  *    must display the following acknowledgement:
   26  *      This product includes software developed by the University of
   27  *      California, Berkeley and its contributors.
   28  * 4. Neither the name of the University nor the names of its contributors
   29  *    may be used to endorse or promote products derived from this software
   30  *    without specific prior written permission.
   31  *
   32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   42  * SUCH DAMAGE.
   43  *
   44  * from: Header: sun_misc.c,v 1.16 93/04/07 02:46:27 torek Exp 
   45  *
   46  *      @(#)sun_misc.c  8.1 (Berkeley) 6/18/93
   47  */
   48 
   49 #include <sys/cdefs.h>
   50 __FBSDID("$FreeBSD$");
   51 
   52 /*
   53  * IBCS2 compatibility module.
   54  *
   55  * IBCS2 system calls that are implemented differently in BSD are
   56  * handled here.
   57  */
   58 #include <sys/param.h>
   59 #include <sys/systm.h>
   60 #include <sys/capsicum.h>
   61 #include <sys/dirent.h>
   62 #include <sys/fcntl.h>
   63 #include <sys/filedesc.h>
   64 #include <sys/imgact.h>
   65 #include <sys/kernel.h>
   66 #include <sys/lock.h>
   67 #include <sys/malloc.h>
   68 #include <sys/file.h>                   /* Must come after sys/malloc.h */
   69 #include <sys/mutex.h>
   70 #include <sys/namei.h>
   71 #include <sys/priv.h>
   72 #include <sys/reboot.h>
   73 #include <sys/resourcevar.h>
   74 #include <sys/stat.h>
   75 #include <sys/sysctl.h>
   76 #include <sys/syscallsubr.h>
   77 #include <sys/sysproto.h>
   78 #include <sys/time.h>
   79 #include <sys/times.h>
   80 #include <sys/vnode.h>
   81 #include <sys/wait.h>
   82 
   83 #include <machine/cpu.h>
   84 
   85 #include <i386/ibcs2/ibcs2_dirent.h>
   86 #include <i386/ibcs2/ibcs2_signal.h>
   87 #include <i386/ibcs2/ibcs2_proto.h>
   88 #include <i386/ibcs2/ibcs2_unistd.h>
   89 #include <i386/ibcs2/ibcs2_util.h>
   90 #include <i386/ibcs2/ibcs2_utime.h>
   91 #include <i386/ibcs2/ibcs2_xenix.h>
   92 
   93 #include <security/mac/mac_framework.h>
   94 
   95 int
   96 ibcs2_ulimit(td, uap)
   97         struct thread *td;
   98         struct ibcs2_ulimit_args *uap;
   99 {
  100         struct rlimit rl;
  101         struct proc *p;
  102         int error;
  103 #define IBCS2_GETFSIZE          1
  104 #define IBCS2_SETFSIZE          2
  105 #define IBCS2_GETPSIZE          3
  106 #define IBCS2_GETDTABLESIZE     4
  107 
  108         p = td->td_proc;
  109         switch (uap->cmd) {
  110         case IBCS2_GETFSIZE:
  111                 PROC_LOCK(p);
  112                 td->td_retval[0] = lim_cur(p, RLIMIT_FSIZE);
  113                 PROC_UNLOCK(p);
  114                 if (td->td_retval[0] == -1)
  115                         td->td_retval[0] = 0x7fffffff;
  116                 return 0;
  117         case IBCS2_SETFSIZE:
  118                 PROC_LOCK(p);
  119                 rl.rlim_max = lim_max(p, RLIMIT_FSIZE);
  120                 PROC_UNLOCK(p);
  121                 rl.rlim_cur = uap->newlimit;
  122                 error = kern_setrlimit(td, RLIMIT_FSIZE, &rl);
  123                 if (!error) {
  124                         PROC_LOCK(p);
  125                         td->td_retval[0] = lim_cur(p, RLIMIT_FSIZE);
  126                         PROC_UNLOCK(p);
  127                 } else {
  128                         DPRINTF(("failed "));
  129                 }
  130                 return error;
  131         case IBCS2_GETPSIZE:
  132                 PROC_LOCK(p);
  133                 td->td_retval[0] = lim_cur(p, RLIMIT_RSS); /* XXX */
  134                 PROC_UNLOCK(p);
  135                 return 0;
  136         case IBCS2_GETDTABLESIZE:
  137                 uap->cmd = IBCS2_SC_OPEN_MAX;
  138                 return ibcs2_sysconf(td, (struct ibcs2_sysconf_args *)uap);
  139         default:
  140                 return ENOSYS;
  141         }
  142 }
  143 
  144 #define IBCS2_WSTOPPED       0177
  145 #define IBCS2_STOPCODE(sig)  ((sig) << 8 | IBCS2_WSTOPPED)
  146 int
  147 ibcs2_wait(td, uap)
  148         struct thread *td;
  149         struct ibcs2_wait_args *uap;
  150 {
  151         int error, options, status;
  152         int *statusp;
  153         pid_t pid;
  154         struct trapframe *tf = td->td_frame;
  155         
  156         if ((tf->tf_eflags & (PSL_Z|PSL_PF|PSL_N|PSL_V))
  157             == (PSL_Z|PSL_PF|PSL_N|PSL_V)) {
  158                 /* waitpid */
  159                 pid = uap->a1;
  160                 statusp = (int *)uap->a2;
  161                 options = uap->a3;
  162         } else {
  163                 /* wait */
  164                 pid = WAIT_ANY;
  165                 statusp = (int *)uap->a1;
  166                 options = 0;
  167         }
  168         error = kern_wait(td, pid, &status, options, NULL);
  169         if (error)
  170                 return error;
  171         if (statusp) {
  172                 /*
  173                  * Convert status/signal result.
  174                  */
  175                 if (WIFSTOPPED(status)) {
  176                         if (WSTOPSIG(status) <= 0 ||
  177                             WSTOPSIG(status) > IBCS2_SIGTBLSZ)
  178                                 return (EINVAL);
  179                         status =
  180                           IBCS2_STOPCODE(bsd_to_ibcs2_sig[_SIG_IDX(WSTOPSIG(status))]);
  181                 } else if (WIFSIGNALED(status)) {
  182                         if (WTERMSIG(status) <= 0 ||
  183                             WTERMSIG(status) > IBCS2_SIGTBLSZ)
  184                                 return (EINVAL);
  185                         status = bsd_to_ibcs2_sig[_SIG_IDX(WTERMSIG(status))];
  186                 }
  187                 /* else exit status -- identical */
  188 
  189                 /* record result/status */
  190                 td->td_retval[1] = status;
  191                 return copyout(&status, statusp, sizeof(status));
  192         }
  193 
  194         return 0;
  195 }
  196 
  197 int
  198 ibcs2_execv(td, uap)
  199         struct thread *td;
  200         struct ibcs2_execv_args *uap;
  201 {
  202         struct image_args eargs;
  203         struct vmspace *oldvmspace;
  204         char *path;
  205         int error;
  206 
  207         CHECKALTEXIST(td, uap->path, &path);
  208 
  209         error = pre_execve(td, &oldvmspace);
  210         if (error != 0) {
  211                 free(path, M_TEMP);
  212                 return (error);
  213         }
  214         error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp, NULL);
  215         free(path, M_TEMP);
  216         if (error == 0)
  217                 error = kern_execve(td, &eargs, NULL);
  218         post_execve(td, error, oldvmspace);
  219         return (error);
  220 }
  221 
  222 int
  223 ibcs2_execve(td, uap) 
  224         struct thread *td;
  225         struct ibcs2_execve_args *uap;
  226 {
  227         struct image_args eargs;
  228         struct vmspace *oldvmspace;
  229         char *path;
  230         int error;
  231 
  232         CHECKALTEXIST(td, uap->path, &path);
  233 
  234         error = pre_execve(td, &oldvmspace);
  235         if (error != 0) {
  236                 free(path, M_TEMP);
  237                 return (error);
  238         }
  239         error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp,
  240             uap->envp);
  241         free(path, M_TEMP);
  242         if (error == 0)
  243                 error = kern_execve(td, &eargs, NULL);
  244         post_execve(td, error, oldvmspace);
  245         return (error);
  246 }
  247 
  248 int
  249 ibcs2_umount(td, uap)
  250         struct thread *td;
  251         struct ibcs2_umount_args *uap;
  252 {
  253         struct unmount_args um;
  254 
  255         um.path = uap->name;
  256         um.flags = 0;
  257         return sys_unmount(td, &um);
  258 }
  259 
  260 int
  261 ibcs2_mount(td, uap)
  262         struct thread *td;
  263         struct ibcs2_mount_args *uap;
  264 {
  265 #ifdef notyet
  266         int oflags = uap->flags, nflags, error;
  267         char fsname[MFSNAMELEN];
  268 
  269         if (oflags & (IBCS2_MS_NOSUB | IBCS2_MS_SYS5))
  270                 return (EINVAL);
  271         if ((oflags & IBCS2_MS_NEWTYPE) == 0)
  272                 return (EINVAL);
  273         nflags = 0;
  274         if (oflags & IBCS2_MS_RDONLY)
  275                 nflags |= MNT_RDONLY;
  276         if (oflags & IBCS2_MS_NOSUID)
  277                 nflags |= MNT_NOSUID;
  278         if (oflags & IBCS2_MS_REMOUNT)
  279                 nflags |= MNT_UPDATE;
  280         uap->flags = nflags;
  281 
  282         if (error = copyinstr((caddr_t)uap->type, fsname, sizeof fsname,
  283                               (u_int *)0))
  284                 return (error);
  285 
  286         if (strcmp(fsname, "4.2") == 0) {
  287                 uap->type = (caddr_t)STACK_ALLOC();
  288                 if (error = copyout("ufs", uap->type, sizeof("ufs")))
  289                         return (error);
  290         } else if (strcmp(fsname, "nfs") == 0) {
  291                 struct ibcs2_nfs_args sna;
  292                 struct sockaddr_in sain;
  293                 struct nfs_args na;
  294                 struct sockaddr sa;
  295 
  296                 if (error = copyin(uap->data, &sna, sizeof sna))
  297                         return (error);
  298                 if (error = copyin(sna.addr, &sain, sizeof sain))
  299                         return (error);
  300                 bcopy(&sain, &sa, sizeof sa);
  301                 sa.sa_len = sizeof(sain);
  302                 uap->data = (caddr_t)STACK_ALLOC();
  303                 na.addr = (struct sockaddr *)((int)uap->data + sizeof na);
  304                 na.sotype = SOCK_DGRAM;
  305                 na.proto = IPPROTO_UDP;
  306                 na.fh = (nfsv2fh_t *)sna.fh;
  307                 na.flags = sna.flags;
  308                 na.wsize = sna.wsize;
  309                 na.rsize = sna.rsize;
  310                 na.timeo = sna.timeo;
  311                 na.retrans = sna.retrans;
  312                 na.hostname = sna.hostname;
  313 
  314                 if (error = copyout(&sa, na.addr, sizeof sa))
  315                         return (error);
  316                 if (error = copyout(&na, uap->data, sizeof na))
  317                         return (error);
  318         }
  319         return (mount(td, uap));
  320 #else
  321         return EINVAL;
  322 #endif
  323 }
  324 
  325 /*
  326  * Read iBCS2-style directory entries.  We suck them into kernel space so
  327  * that they can be massaged before being copied out to user code.  Like
  328  * SunOS, we squish out `empty' entries.
  329  *
  330  * This is quite ugly, but what do you expect from compatibility code?
  331  */
  332 
  333 int
  334 ibcs2_getdents(td, uap)
  335         struct thread *td;
  336         register struct ibcs2_getdents_args *uap;
  337 {
  338         register struct vnode *vp;
  339         register caddr_t inp, buf;      /* BSD-format */
  340         register int len, reclen;       /* BSD-format */
  341         register caddr_t outp;          /* iBCS2-format */
  342         register int resid;             /* iBCS2-format */
  343         cap_rights_t rights;
  344         struct file *fp;
  345         struct uio auio;
  346         struct iovec aiov;
  347         struct ibcs2_dirent idb;
  348         off_t off;                      /* true file offset */
  349         int buflen, error, eofflag;
  350         u_long *cookies = NULL, *cookiep;
  351         int ncookies;
  352 #define BSD_DIRENT(cp)          ((struct dirent *)(cp))
  353 #define IBCS2_RECLEN(reclen)    (reclen + sizeof(u_short))
  354 
  355         memset(&idb, 0, sizeof(idb));
  356         error = getvnode(td->td_proc->p_fd, uap->fd,
  357             cap_rights_init(&rights, CAP_READ), &fp);
  358         if (error != 0)
  359                 return (error);
  360         if ((fp->f_flag & FREAD) == 0) {
  361                 fdrop(fp, td);
  362                 return (EBADF);
  363         }
  364         vp = fp->f_vnode;
  365         if (vp->v_type != VDIR) {       /* XXX  vnode readdir op should do this */
  366                 fdrop(fp, td);
  367                 return (EINVAL);
  368         }
  369 
  370         off = fp->f_offset;
  371 #define DIRBLKSIZ       512             /* XXX we used to use ufs's DIRBLKSIZ */
  372         buflen = max(DIRBLKSIZ, uap->nbytes);
  373         buflen = min(buflen, MAXBSIZE);
  374         buf = malloc(buflen, M_TEMP, M_WAITOK);
  375         vn_lock(vp, LK_SHARED | LK_RETRY);
  376 again:
  377         aiov.iov_base = buf;
  378         aiov.iov_len = buflen;
  379         auio.uio_iov = &aiov;
  380         auio.uio_iovcnt = 1;
  381         auio.uio_rw = UIO_READ;
  382         auio.uio_segflg = UIO_SYSSPACE;
  383         auio.uio_td = td;
  384         auio.uio_resid = buflen;
  385         auio.uio_offset = off;
  386 
  387         if (cookies) {
  388                 free(cookies, M_TEMP);
  389                 cookies = NULL;
  390         }
  391 
  392 #ifdef MAC
  393         error = mac_vnode_check_readdir(td->td_ucred, vp);
  394         if (error)
  395                 goto out;
  396 #endif
  397 
  398         /*
  399          * First we read into the malloc'ed buffer, then
  400          * we massage it into user space, one record at a time.
  401          */
  402         if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies, &cookies)) != 0)
  403                 goto out;
  404         inp = buf;
  405         outp = uap->buf;
  406         resid = uap->nbytes;
  407         if ((len = buflen - auio.uio_resid) <= 0)
  408                 goto eof;
  409 
  410         cookiep = cookies;
  411 
  412         if (cookies) {
  413                 /*
  414                  * When using cookies, the vfs has the option of reading from
  415                  * a different offset than that supplied (UFS truncates the
  416                  * offset to a block boundary to make sure that it never reads
  417                  * partway through a directory entry, even if the directory
  418                  * has been compacted).
  419                  */
  420                 while (len > 0 && ncookies > 0 && *cookiep <= off) {
  421                         len -= BSD_DIRENT(inp)->d_reclen;
  422                         inp += BSD_DIRENT(inp)->d_reclen;
  423                         cookiep++;
  424                         ncookies--;
  425                 }
  426         }
  427 
  428         for (; len > 0; len -= reclen) {
  429                 if (cookiep && ncookies == 0)
  430                         break;
  431                 reclen = BSD_DIRENT(inp)->d_reclen;
  432                 if (reclen & 3) {
  433                         printf("ibcs2_getdents: reclen=%d\n", reclen);
  434                         error = EFAULT;
  435                         goto out;
  436                 }
  437                 if (BSD_DIRENT(inp)->d_fileno == 0) {
  438                         inp += reclen;  /* it is a hole; squish it out */
  439                         if (cookiep) {
  440                                 off = *cookiep++;
  441                                 ncookies--;
  442                         } else
  443                                 off += reclen;
  444                         continue;
  445                 }
  446                 if (reclen > len || resid < IBCS2_RECLEN(reclen)) {
  447                         /* entry too big for buffer, so just stop */
  448                         outp++;
  449                         break;
  450                 }
  451                 /*
  452                  * Massage in place to make an iBCS2-shaped dirent (otherwise
  453                  * we have to worry about touching user memory outside of
  454                  * the copyout() call).
  455                  */
  456                 idb.d_ino = (ibcs2_ino_t)BSD_DIRENT(inp)->d_fileno;
  457                 idb.d_off = (ibcs2_off_t)off;
  458                 idb.d_reclen = (u_short)IBCS2_RECLEN(reclen);
  459                 if ((error = copyout((caddr_t)&idb, outp, 10)) != 0 ||
  460                     (error = copyout(BSD_DIRENT(inp)->d_name, outp + 10,
  461                                      BSD_DIRENT(inp)->d_namlen + 1)) != 0)
  462                         goto out;
  463                 /* advance past this real entry */
  464                 if (cookiep) {
  465                         off = *cookiep++;
  466                         ncookies--;
  467                 } else
  468                         off += reclen;
  469                 inp += reclen;
  470                 /* advance output past iBCS2-shaped entry */
  471                 outp += IBCS2_RECLEN(reclen);
  472                 resid -= IBCS2_RECLEN(reclen);
  473         }
  474         /* if we squished out the whole block, try again */
  475         if (outp == uap->buf)
  476                 goto again;
  477         fp->f_offset = off;             /* update the vnode offset */
  478 eof:
  479         td->td_retval[0] = uap->nbytes - resid;
  480 out:
  481         VOP_UNLOCK(vp, 0);
  482         fdrop(fp, td);
  483         if (cookies)
  484                 free(cookies, M_TEMP);
  485         free(buf, M_TEMP);
  486         return (error);
  487 }
  488 
  489 int
  490 ibcs2_read(td, uap)
  491         struct thread *td;
  492         struct ibcs2_read_args *uap;
  493 {
  494         register struct vnode *vp;
  495         register caddr_t inp, buf;      /* BSD-format */
  496         register int len, reclen;       /* BSD-format */
  497         register caddr_t outp;          /* iBCS2-format */
  498         register int resid;             /* iBCS2-format */
  499         cap_rights_t rights;
  500         struct file *fp;
  501         struct uio auio;
  502         struct iovec aiov;
  503         struct ibcs2_direct {
  504                 ibcs2_ino_t ino;
  505                 char name[14];
  506         } idb;
  507         off_t off;                      /* true file offset */
  508         int buflen, error, eofflag, size;
  509         u_long *cookies = NULL, *cookiep;
  510         int ncookies;
  511 
  512         error = getvnode(td->td_proc->p_fd, uap->fd,
  513             cap_rights_init(&rights, CAP_READ), &fp);
  514         if (error != 0) {
  515                 if (error == EINVAL)
  516                         return sys_read(td, (struct read_args *)uap);
  517                 else
  518                         return error;
  519         }
  520         if ((fp->f_flag & FREAD) == 0) {
  521                 fdrop(fp, td);
  522                 return (EBADF);
  523         }
  524         vp = fp->f_vnode;
  525         if (vp->v_type != VDIR) {
  526                 fdrop(fp, td);
  527                 return sys_read(td, (struct read_args *)uap);
  528         }
  529 
  530         off = fp->f_offset;
  531 
  532         DPRINTF(("ibcs2_read: read directory\n"));
  533 
  534         buflen = max(DIRBLKSIZ, uap->nbytes);
  535         buflen = min(buflen, MAXBSIZE);
  536         buf = malloc(buflen, M_TEMP, M_WAITOK);
  537         vn_lock(vp, LK_SHARED | LK_RETRY);
  538 again:
  539         aiov.iov_base = buf;
  540         aiov.iov_len = buflen;
  541         auio.uio_iov = &aiov;
  542         auio.uio_iovcnt = 1;
  543         auio.uio_rw = UIO_READ;
  544         auio.uio_segflg = UIO_SYSSPACE;
  545         auio.uio_td = td;
  546         auio.uio_resid = buflen;
  547         auio.uio_offset = off;
  548 
  549         if (cookies) {
  550                 free(cookies, M_TEMP);
  551                 cookies = NULL;
  552         }
  553 
  554 #ifdef MAC
  555         error = mac_vnode_check_readdir(td->td_ucred, vp);
  556         if (error)
  557                 goto out;
  558 #endif
  559 
  560         /*
  561          * First we read into the malloc'ed buffer, then
  562          * we massage it into user space, one record at a time.
  563          */
  564         if ((error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies, &cookies)) != 0) {
  565                 DPRINTF(("VOP_READDIR failed: %d\n", error));
  566                 goto out;
  567         }
  568         inp = buf;
  569         outp = uap->buf;
  570         resid = uap->nbytes;
  571         if ((len = buflen - auio.uio_resid) <= 0)
  572                 goto eof;
  573 
  574         cookiep = cookies;
  575 
  576         if (cookies) {
  577                 /*
  578                  * When using cookies, the vfs has the option of reading from
  579                  * a different offset than that supplied (UFS truncates the
  580                  * offset to a block boundary to make sure that it never reads
  581                  * partway through a directory entry, even if the directory
  582                  * has been compacted).
  583                  */
  584                 while (len > 0 && ncookies > 0 && *cookiep <= off) {
  585                         len -= BSD_DIRENT(inp)->d_reclen;
  586                         inp += BSD_DIRENT(inp)->d_reclen;
  587                         cookiep++;
  588                         ncookies--;
  589                 }
  590         }
  591 
  592         for (; len > 0 && resid > 0; len -= reclen) {
  593                 if (cookiep && ncookies == 0)
  594                         break;
  595                 reclen = BSD_DIRENT(inp)->d_reclen;
  596                 if (reclen & 3) {
  597                         printf("ibcs2_read: reclen=%d\n", reclen);
  598                         error = EFAULT;
  599                         goto out;
  600                 }
  601                 if (BSD_DIRENT(inp)->d_fileno == 0) {
  602                         inp += reclen;  /* it is a hole; squish it out */
  603                         if (cookiep) {
  604                                 off = *cookiep++;
  605                                 ncookies--;
  606                         } else
  607                                 off += reclen;
  608                         continue;
  609                 }
  610                 if (reclen > len || resid < sizeof(struct ibcs2_direct)) {
  611                         /* entry too big for buffer, so just stop */
  612                         outp++;
  613                         break;
  614                 }
  615                 /*
  616                  * Massage in place to make an iBCS2-shaped dirent (otherwise
  617                  * we have to worry about touching user memory outside of
  618                  * the copyout() call).
  619                  *
  620                  * TODO: if length(filename) > 14, then break filename into
  621                  * multiple entries and set inode = 0xffff except last
  622                  */
  623                 idb.ino = (BSD_DIRENT(inp)->d_fileno > 0xfffe) ? 0xfffe :
  624                         BSD_DIRENT(inp)->d_fileno;
  625                 (void)copystr(BSD_DIRENT(inp)->d_name, idb.name, 14, &size);
  626                 bzero(idb.name + size, 14 - size);
  627                 if ((error = copyout(&idb, outp, sizeof(struct ibcs2_direct))) != 0)
  628                         goto out;
  629                 /* advance past this real entry */
  630                 if (cookiep) {
  631                         off = *cookiep++;
  632                         ncookies--;
  633                 } else
  634                         off += reclen;
  635                 inp += reclen;
  636                 /* advance output past iBCS2-shaped entry */
  637                 outp += sizeof(struct ibcs2_direct);
  638                 resid -= sizeof(struct ibcs2_direct);
  639         }
  640         /* if we squished out the whole block, try again */
  641         if (outp == uap->buf)
  642                 goto again;
  643         fp->f_offset = off;             /* update the vnode offset */
  644 eof:
  645         td->td_retval[0] = uap->nbytes - resid;
  646 out:
  647         VOP_UNLOCK(vp, 0);
  648         fdrop(fp, td);
  649         if (cookies)
  650                 free(cookies, M_TEMP);
  651         free(buf, M_TEMP);
  652         return (error);
  653 }
  654 
  655 int
  656 ibcs2_mknod(td, uap)
  657         struct thread *td;
  658         struct ibcs2_mknod_args *uap;
  659 {
  660         char *path;
  661         int error;
  662 
  663         CHECKALTCREAT(td, uap->path, &path);
  664         if (S_ISFIFO(uap->mode))
  665                 error = kern_mkfifo(td, path, UIO_SYSSPACE, uap->mode);
  666         else
  667                 error = kern_mknod(td, path, UIO_SYSSPACE, uap->mode, uap->dev);
  668         free(path, M_TEMP);
  669         return (error);
  670 }
  671 
  672 int
  673 ibcs2_getgroups(td, uap)
  674         struct thread *td;
  675         struct ibcs2_getgroups_args *uap;
  676 {
  677         ibcs2_gid_t *iset;
  678         gid_t *gp;
  679         u_int i, ngrp;
  680         int error;
  681 
  682         if (uap->gidsetsize < td->td_ucred->cr_ngroups) {
  683                 if (uap->gidsetsize == 0)
  684                         ngrp = 0;
  685                 else
  686                         return (EINVAL);
  687         } else
  688                 ngrp = td->td_ucred->cr_ngroups;
  689         gp = malloc(ngrp * sizeof(*gp), M_TEMP, M_WAITOK);
  690         error = kern_getgroups(td, &ngrp, gp);
  691         if (error)
  692                 goto out;
  693         if (uap->gidsetsize > 0) {
  694                 iset = malloc(ngrp * sizeof(*iset), M_TEMP, M_WAITOK);
  695                 for (i = 0; i < ngrp; i++)
  696                         iset[i] = (ibcs2_gid_t)gp[i];
  697                 error = copyout(iset, uap->gidset, ngrp * sizeof(ibcs2_gid_t));
  698                 free(iset, M_TEMP);
  699         }
  700         if (error == 0)
  701                 td->td_retval[0] = ngrp;
  702 out:
  703         free(gp, M_TEMP);
  704         return (error);
  705 }
  706 
  707 int
  708 ibcs2_setgroups(td, uap)
  709         struct thread *td;
  710         struct ibcs2_setgroups_args *uap;
  711 {
  712         ibcs2_gid_t *iset;
  713         gid_t *gp;
  714         int error, i;
  715 
  716         if (uap->gidsetsize < 0 || uap->gidsetsize > ngroups_max + 1)
  717                 return (EINVAL);
  718         if (uap->gidsetsize && uap->gidset == NULL)
  719                 return (EINVAL);
  720         gp = malloc(uap->gidsetsize * sizeof(*gp), M_TEMP, M_WAITOK);
  721         if (uap->gidsetsize) {
  722                 iset = malloc(uap->gidsetsize * sizeof(*iset), M_TEMP, M_WAITOK);
  723                 error = copyin(uap->gidset, iset, sizeof(ibcs2_gid_t) *
  724                     uap->gidsetsize);
  725                 if (error) {
  726                         free(iset, M_TEMP);
  727                         goto out;
  728                 }
  729                 for (i = 0; i < uap->gidsetsize; i++)
  730                         gp[i] = (gid_t)iset[i];
  731         }
  732 
  733         error = kern_setgroups(td, uap->gidsetsize, gp);
  734 out:
  735         free(gp, M_TEMP);
  736         return (error);
  737 }
  738 
  739 int
  740 ibcs2_setuid(td, uap)
  741         struct thread *td;
  742         struct ibcs2_setuid_args *uap;
  743 {
  744         struct setuid_args sa;
  745 
  746         sa.uid = (uid_t)uap->uid;
  747         return sys_setuid(td, &sa);
  748 }
  749 
  750 int
  751 ibcs2_setgid(td, uap)
  752         struct thread *td;
  753         struct ibcs2_setgid_args *uap;
  754 {
  755         struct setgid_args sa;
  756 
  757         sa.gid = (gid_t)uap->gid;
  758         return sys_setgid(td, &sa);
  759 }
  760 
  761 int
  762 ibcs2_time(td, uap)
  763         struct thread *td;
  764         struct ibcs2_time_args *uap;
  765 {
  766         struct timeval tv;
  767 
  768         microtime(&tv);
  769         td->td_retval[0] = tv.tv_sec;
  770         if (uap->tp)
  771                 return copyout((caddr_t)&tv.tv_sec, (caddr_t)uap->tp,
  772                                sizeof(ibcs2_time_t));
  773         else
  774                 return 0;
  775 }
  776 
  777 int
  778 ibcs2_pathconf(td, uap)
  779         struct thread *td;
  780         struct ibcs2_pathconf_args *uap;
  781 {
  782         char *path;
  783         int error;
  784 
  785         CHECKALTEXIST(td, uap->path, &path);
  786         uap->name++;    /* iBCS2 _PC_* defines are offset by one */
  787         error = kern_pathconf(td, path, UIO_SYSSPACE, uap->name, FOLLOW);
  788         free(path, M_TEMP);
  789         return (error);
  790 }
  791 
  792 int
  793 ibcs2_fpathconf(td, uap)
  794         struct thread *td;
  795         struct ibcs2_fpathconf_args *uap;
  796 {
  797         uap->name++;    /* iBCS2 _PC_* defines are offset by one */
  798         return sys_fpathconf(td, (struct fpathconf_args *)uap);
  799 }
  800 
  801 int
  802 ibcs2_sysconf(td, uap)
  803         struct thread *td;
  804         struct ibcs2_sysconf_args *uap;
  805 {
  806         int mib[2], value, len, error;
  807         struct proc *p;
  808 
  809         p = td->td_proc;
  810         switch(uap->name) {
  811         case IBCS2_SC_ARG_MAX:
  812                 mib[1] = KERN_ARGMAX;
  813                 break;
  814 
  815         case IBCS2_SC_CHILD_MAX:
  816                 PROC_LOCK(p);
  817                 td->td_retval[0] = lim_cur(td->td_proc, RLIMIT_NPROC);
  818                 PROC_UNLOCK(p);
  819                 return 0;
  820 
  821         case IBCS2_SC_CLK_TCK:
  822                 td->td_retval[0] = hz;
  823                 return 0;
  824 
  825         case IBCS2_SC_NGROUPS_MAX:
  826                 mib[1] = KERN_NGROUPS;
  827                 break;
  828 
  829         case IBCS2_SC_OPEN_MAX:
  830                 PROC_LOCK(p);
  831                 td->td_retval[0] = lim_cur(td->td_proc, RLIMIT_NOFILE);
  832                 PROC_UNLOCK(p);
  833                 return 0;
  834                 
  835         case IBCS2_SC_JOB_CONTROL:
  836                 mib[1] = KERN_JOB_CONTROL;
  837                 break;
  838                 
  839         case IBCS2_SC_SAVED_IDS:
  840                 mib[1] = KERN_SAVED_IDS;
  841                 break;
  842                 
  843         case IBCS2_SC_VERSION:
  844                 mib[1] = KERN_POSIX1;
  845                 break;
  846                 
  847         case IBCS2_SC_PASS_MAX:
  848                 td->td_retval[0] = 128;         /* XXX - should we create PASS_MAX ? */
  849                 return 0;
  850 
  851         case IBCS2_SC_XOPEN_VERSION:
  852                 td->td_retval[0] = 2;           /* XXX: What should that be? */
  853                 return 0;
  854                 
  855         default:
  856                 return EINVAL;
  857         }
  858 
  859         mib[0] = CTL_KERN;
  860         len = sizeof(value);
  861         error = kernel_sysctl(td, mib, 2, &value, &len, NULL, 0, NULL, 0);
  862         if (error)
  863                 return error;
  864         td->td_retval[0] = value;
  865         return 0;
  866 }
  867 
  868 int
  869 ibcs2_alarm(td, uap)
  870         struct thread *td;
  871         struct ibcs2_alarm_args *uap;
  872 {
  873         struct itimerval itv, oitv;
  874         int error;
  875 
  876         timevalclear(&itv.it_interval);
  877         itv.it_value.tv_sec = uap->sec;
  878         itv.it_value.tv_usec = 0;
  879         error = kern_setitimer(td, ITIMER_REAL, &itv, &oitv);
  880         if (error)
  881                 return (error);
  882         if (oitv.it_value.tv_usec != 0)
  883                 oitv.it_value.tv_sec++;
  884         td->td_retval[0] = oitv.it_value.tv_sec;
  885         return (0);
  886 }
  887 
  888 int
  889 ibcs2_times(td, uap)
  890         struct thread *td;
  891         struct ibcs2_times_args *uap;
  892 {
  893         struct rusage ru;
  894         struct timeval t;
  895         struct tms tms;
  896         int error;
  897 
  898 #define CONVTCK(r)      (r.tv_sec * hz + r.tv_usec / (1000000 / hz))
  899 
  900         error = kern_getrusage(td, RUSAGE_SELF, &ru);
  901         if (error)
  902                 return (error);
  903         tms.tms_utime = CONVTCK(ru.ru_utime);
  904         tms.tms_stime = CONVTCK(ru.ru_stime);
  905 
  906         error = kern_getrusage(td, RUSAGE_CHILDREN, &ru);
  907         if (error)
  908                 return (error);
  909         tms.tms_cutime = CONVTCK(ru.ru_utime);
  910         tms.tms_cstime = CONVTCK(ru.ru_stime);
  911 
  912         microtime(&t);
  913         td->td_retval[0] = CONVTCK(t);
  914         
  915         return (copyout(&tms, uap->tp, sizeof(struct tms)));
  916 }
  917 
  918 int
  919 ibcs2_stime(td, uap)
  920         struct thread *td;
  921         struct ibcs2_stime_args *uap;
  922 {
  923         struct timeval tv;
  924         long secs;
  925         int error;
  926 
  927         error = copyin(uap->timep, &secs, sizeof(long));
  928         if (error)
  929                 return (error);
  930         tv.tv_sec = secs;
  931         tv.tv_usec = 0;
  932         error = kern_settimeofday(td, &tv, NULL);
  933         if (error)
  934                 error = EPERM;
  935         return (error);
  936 }
  937 
  938 int
  939 ibcs2_utime(td, uap)
  940         struct thread *td;
  941         struct ibcs2_utime_args *uap;
  942 {
  943         struct ibcs2_utimbuf ubuf;
  944         struct timeval tbuf[2], *tp;
  945         char *path;
  946         int error;
  947 
  948         if (uap->buf) {
  949                 error = copyin(uap->buf, &ubuf, sizeof(ubuf));
  950                 if (error)
  951                         return (error);
  952                 tbuf[0].tv_sec = ubuf.actime;
  953                 tbuf[0].tv_usec = 0;
  954                 tbuf[1].tv_sec = ubuf.modtime;
  955                 tbuf[1].tv_usec = 0;
  956                 tp = tbuf;
  957         } else
  958                 tp = NULL;
  959 
  960         CHECKALTEXIST(td, uap->path, &path);
  961         error = kern_utimes(td, path, UIO_SYSSPACE, tp, UIO_SYSSPACE);
  962         free(path, M_TEMP);
  963         return (error);
  964 }
  965 
  966 int
  967 ibcs2_nice(td, uap)
  968         struct thread *td;
  969         struct ibcs2_nice_args *uap;
  970 {
  971         int error;
  972         struct setpriority_args sa;
  973 
  974         sa.which = PRIO_PROCESS;
  975         sa.who = 0;
  976         sa.prio = td->td_proc->p_nice + uap->incr;
  977         if ((error = sys_setpriority(td, &sa)) != 0)
  978                 return EPERM;
  979         td->td_retval[0] = td->td_proc->p_nice;
  980         return 0;
  981 }
  982 
  983 /*
  984  * iBCS2 getpgrp, setpgrp, setsid, and setpgid
  985  */
  986 
  987 int
  988 ibcs2_pgrpsys(td, uap)
  989         struct thread *td;
  990         struct ibcs2_pgrpsys_args *uap;
  991 {
  992         struct proc *p = td->td_proc;
  993         switch (uap->type) {
  994         case 0:                 /* getpgrp */
  995                 PROC_LOCK(p);
  996                 td->td_retval[0] = p->p_pgrp->pg_id;
  997                 PROC_UNLOCK(p);
  998                 return 0;
  999 
 1000         case 1:                 /* setpgrp */
 1001             {
 1002                 struct setpgid_args sa;
 1003 
 1004                 sa.pid = 0;
 1005                 sa.pgid = 0;
 1006                 sys_setpgid(td, &sa);
 1007                 PROC_LOCK(p);
 1008                 td->td_retval[0] = p->p_pgrp->pg_id;
 1009                 PROC_UNLOCK(p);
 1010                 return 0;
 1011             }
 1012 
 1013         case 2:                 /* setpgid */
 1014             {
 1015                 struct setpgid_args sa;
 1016 
 1017                 sa.pid = uap->pid;
 1018                 sa.pgid = uap->pgid;
 1019                 return sys_setpgid(td, &sa);
 1020             }
 1021 
 1022         case 3:                 /* setsid */
 1023                 return sys_setsid(td, NULL);
 1024 
 1025         default:
 1026                 return EINVAL;
 1027         }
 1028 }
 1029 
 1030 /*
 1031  * XXX - need to check for nested calls
 1032  */
 1033 
 1034 int
 1035 ibcs2_plock(td, uap)
 1036         struct thread *td;
 1037         struct ibcs2_plock_args *uap;
 1038 {
 1039         int error;
 1040 #define IBCS2_UNLOCK    0
 1041 #define IBCS2_PROCLOCK  1
 1042 #define IBCS2_TEXTLOCK  2
 1043 #define IBCS2_DATALOCK  4
 1044 
 1045         
 1046         switch(uap->cmd) {
 1047         case IBCS2_UNLOCK:
 1048                 error = priv_check(td, PRIV_VM_MUNLOCK);
 1049                 if (error)
 1050                         return (error);
 1051                 /* XXX - TODO */
 1052                 return (0);
 1053 
 1054         case IBCS2_PROCLOCK:
 1055         case IBCS2_TEXTLOCK:
 1056         case IBCS2_DATALOCK:
 1057                 error = priv_check(td, PRIV_VM_MLOCK);
 1058                 if (error)
 1059                         return (error);
 1060                 /* XXX - TODO */
 1061                 return 0;
 1062         }
 1063         return EINVAL;
 1064 }
 1065 
 1066 int
 1067 ibcs2_uadmin(td, uap)
 1068         struct thread *td;
 1069         struct ibcs2_uadmin_args *uap;
 1070 {
 1071 #define SCO_A_REBOOT        1
 1072 #define SCO_A_SHUTDOWN      2
 1073 #define SCO_A_REMOUNT       4
 1074 #define SCO_A_CLOCK         8
 1075 #define SCO_A_SETCONFIG     128
 1076 #define SCO_A_GETDEV        130
 1077 
 1078 #define SCO_AD_HALT         0
 1079 #define SCO_AD_BOOT         1
 1080 #define SCO_AD_IBOOT        2
 1081 #define SCO_AD_PWRDOWN      3
 1082 #define SCO_AD_PWRNAP       4
 1083 
 1084 #define SCO_AD_PANICBOOT    1
 1085 
 1086 #define SCO_AD_GETBMAJ      0
 1087 #define SCO_AD_GETCMAJ      1
 1088 
 1089         switch(uap->cmd) {
 1090         case SCO_A_REBOOT:
 1091         case SCO_A_SHUTDOWN:
 1092                 switch(uap->func) {
 1093                         struct reboot_args r;
 1094                 case SCO_AD_HALT:
 1095                 case SCO_AD_PWRDOWN:
 1096                 case SCO_AD_PWRNAP:
 1097                         r.opt = RB_HALT;
 1098                         return (sys_reboot(td, &r));
 1099                 case SCO_AD_BOOT:
 1100                 case SCO_AD_IBOOT:
 1101                         r.opt = RB_AUTOBOOT;
 1102                         return (sys_reboot(td, &r));
 1103                 }
 1104                 return EINVAL;
 1105         case SCO_A_REMOUNT:
 1106         case SCO_A_CLOCK:
 1107         case SCO_A_SETCONFIG:
 1108                 return 0;
 1109         case SCO_A_GETDEV:
 1110                 return EINVAL;  /* XXX - TODO */
 1111         }
 1112         return EINVAL;
 1113 }
 1114 
 1115 int
 1116 ibcs2_sysfs(td, uap)
 1117         struct thread *td;
 1118         struct ibcs2_sysfs_args *uap;
 1119 {
 1120 #define IBCS2_GETFSIND        1
 1121 #define IBCS2_GETFSTYP        2
 1122 #define IBCS2_GETNFSTYP       3
 1123 
 1124         switch(uap->cmd) {
 1125         case IBCS2_GETFSIND:
 1126         case IBCS2_GETFSTYP:
 1127         case IBCS2_GETNFSTYP:
 1128                 break;
 1129         }
 1130         return EINVAL;          /* XXX - TODO */
 1131 }
 1132 
 1133 int
 1134 ibcs2_unlink(td, uap)
 1135         struct thread *td;
 1136         struct ibcs2_unlink_args *uap;
 1137 {
 1138         char *path;
 1139         int error;
 1140 
 1141         CHECKALTEXIST(td, uap->path, &path);
 1142         error = kern_unlink(td, path, UIO_SYSSPACE);
 1143         free(path, M_TEMP);
 1144         return (error);
 1145 }
 1146 
 1147 int
 1148 ibcs2_chdir(td, uap)
 1149         struct thread *td;
 1150         struct ibcs2_chdir_args *uap;
 1151 {
 1152         char *path;
 1153         int error;
 1154 
 1155         CHECKALTEXIST(td, uap->path, &path);
 1156         error = kern_chdir(td, path, UIO_SYSSPACE);
 1157         free(path, M_TEMP);
 1158         return (error);
 1159 }
 1160 
 1161 int
 1162 ibcs2_chmod(td, uap)
 1163         struct thread *td;
 1164         struct ibcs2_chmod_args *uap;
 1165 {
 1166         char *path;
 1167         int error;
 1168 
 1169         CHECKALTEXIST(td, uap->path, &path);
 1170         error = kern_chmod(td, path, UIO_SYSSPACE, uap->mode);
 1171         free(path, M_TEMP);
 1172         return (error);
 1173 }
 1174 
 1175 int
 1176 ibcs2_chown(td, uap)
 1177         struct thread *td;
 1178         struct ibcs2_chown_args *uap;
 1179 {
 1180         char *path;
 1181         int error;
 1182 
 1183         CHECKALTEXIST(td, uap->path, &path);
 1184         error = kern_chown(td, path, UIO_SYSSPACE, uap->uid, uap->gid);
 1185         free(path, M_TEMP);
 1186         return (error);
 1187 }
 1188 
 1189 int
 1190 ibcs2_rmdir(td, uap)
 1191         struct thread *td;
 1192         struct ibcs2_rmdir_args *uap;
 1193 {
 1194         char *path;
 1195         int error;
 1196 
 1197         CHECKALTEXIST(td, uap->path, &path);
 1198         error = kern_rmdir(td, path, UIO_SYSSPACE);
 1199         free(path, M_TEMP);
 1200         return (error);
 1201 }
 1202 
 1203 int
 1204 ibcs2_mkdir(td, uap)
 1205         struct thread *td;
 1206         struct ibcs2_mkdir_args *uap;
 1207 {
 1208         char *path;
 1209         int error;
 1210 
 1211         CHECKALTEXIST(td, uap->path, &path);
 1212         error = kern_mkdir(td, path, UIO_SYSSPACE, uap->mode);
 1213         free(path, M_TEMP);
 1214         return (error);
 1215 }
 1216 
 1217 int
 1218 ibcs2_symlink(td, uap)
 1219         struct thread *td;
 1220         struct ibcs2_symlink_args *uap;
 1221 {
 1222         char *path, *link;
 1223         int error;
 1224 
 1225         CHECKALTEXIST(td, uap->path, &path);
 1226 
 1227         /*
 1228          * Have to expand CHECKALTCREAT() so that 'path' can be freed on
 1229          * errors.
 1230          */
 1231         error = ibcs2_emul_find(td, uap->link, UIO_USERSPACE, &link, 1);
 1232         if (link == NULL) {
 1233                 free(path, M_TEMP);
 1234                 return (error);
 1235         }
 1236         error = kern_symlink(td, path, link, UIO_SYSSPACE);
 1237         free(path, M_TEMP);
 1238         free(link, M_TEMP);
 1239         return (error);
 1240 }
 1241 
 1242 int
 1243 ibcs2_rename(td, uap)
 1244         struct thread *td;
 1245         struct ibcs2_rename_args *uap;
 1246 {
 1247         char *from, *to;
 1248         int error;
 1249 
 1250         CHECKALTEXIST(td, uap->from, &from);
 1251 
 1252         /*
 1253          * Have to expand CHECKALTCREAT() so that 'from' can be freed on
 1254          * errors.
 1255          */
 1256         error = ibcs2_emul_find(td, uap->to, UIO_USERSPACE, &to, 1);
 1257         if (to == NULL) {
 1258                 free(from, M_TEMP);
 1259                 return (error);
 1260         }
 1261         error = kern_rename(td, from, to, UIO_SYSSPACE);
 1262         free(from, M_TEMP);
 1263         free(to, M_TEMP);
 1264         return (error);
 1265 }
 1266 
 1267 int
 1268 ibcs2_readlink(td, uap)
 1269         struct thread *td;
 1270         struct ibcs2_readlink_args *uap;
 1271 {
 1272         char *path;
 1273         int error;
 1274 
 1275         CHECKALTEXIST(td, uap->path, &path);
 1276         error = kern_readlink(td, path, UIO_SYSSPACE, uap->buf, UIO_USERSPACE,
 1277                 uap->count);
 1278         free(path, M_TEMP);
 1279         return (error);
 1280 }

Cache object: f9352a65d01441131dc0c09cd659a2bb


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.