[BACK]Return to filesys.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rdistd

Annotation of src/usr.bin/rdistd/filesys.c, Revision 1.15

1.15    ! guenther    1: /*     $OpenBSD: filesys.c,v 1.14 2014/07/05 07:58:41 guenther Exp $   */
1.3       deraadt     2:
1.1       dm          3: /*
                      4:  * Copyright (c) 1983 Regents of the University of California.
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.10      millert    15:  * 3. Neither the name of the University nor the names of its contributors
1.1       dm         16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
1.15    ! guenther   32: #include <sys/param.h>
        !            33: #include <sys/mount.h>
        !            34:
1.9       millert    35: #include "defs.h"
1.1       dm         36:
                     37: /*
                     38:  * This file contains functions dealing with getting info
                     39:  * about mounted filesystems.
                     40:  */
                     41:
                     42:
                     43: jmp_buf env;
                     44:
                     45: /*
                     46:  * Given a pathname, find the fullest component that exists.
                     47:  * If statbuf is not NULL, set it to point at our stat buffer.
                     48:  */
1.9       millert    49: char *
                     50: find_file(char *pathname, struct stat *statbuf, int *isvalid)
1.1       dm         51: {
                     52:        static char last_pathname[MAXPATHLEN];
                     53:        static char file[MAXPATHLEN + 3];
                     54:        static struct stat filestat;
1.7       mpech      55:        char *p;
1.1       dm         56:
                     57:        /*
                     58:         * Mark the statbuf as invalid to start with.
                     59:         */
                     60:        *isvalid = 0;
                     61:
                     62:        /*
                     63:         * If this is the same pathname as the last time, and
                     64:         * the file buffer is valid and we're doing the same stat()
                     65:         * or lstat(), then set statbuf to the last filestat and
                     66:         * return the last file we found.
                     67:         */
                     68:        if (strcmp(pathname, last_pathname) == 0 && file[0]) {
                     69:                if (statbuf)
                     70:                        statbuf = &filestat;
                     71:                if (strcmp(pathname, file) == 0)
                     72:                        *isvalid = 1;
                     73:                return(file);
                     74:        }
                     75:
1.9       millert    76:        if (strlen(pathname) > sizeof(file) + 3) {
1.1       dm         77:                error("%s: Name to large for buffer.", pathname);
1.5       millert    78:                return(NULL);
1.1       dm         79:        }
                     80:
                     81:        /*
                     82:         * Save for next time
                     83:         */
1.9       millert    84:        (void) strlcpy(last_pathname, pathname, sizeof(last_pathname));
1.1       dm         85:
                     86:        if (*pathname == '/')
1.9       millert    87:                (void) strlcpy(file, pathname, sizeof(file));
1.1       dm         88:        else {
                     89:                /*
                     90:                 * Ensure we have a directory (".") in our path
                     91:                 * so we have something to stat in case the file
                     92:                 * does not exist.
                     93:                 */
1.9       millert    94:                (void) strlcpy(file, "./", sizeof(file));
                     95:                (void) strlcat(file, pathname, sizeof(file));
1.1       dm         96:        }
                     97:
                     98:        while (lstat(file, &filestat) != 0) {
                     99:                /*
                    100:                 * Trim the last part of the pathname to try next level up
                    101:                 */
                    102:                if (errno == ENOENT) {
                    103:                        /*
                    104:                         * Trim file name to get directory name.
                    105:                         * Normally we want to change /dir1/dir2/file
                    106:                         * into "/dir1/dir2/."
                    107:                         */
1.9       millert   108:                        if ((p = (char *) strrchr(file, '/')) != NULL) {
1.4       millert   109:                                if (strcmp(p, "/.") == 0) {
1.9       millert   110:                                        *p = CNULL;
1.4       millert   111:                                } else {
1.9       millert   112:                                        *++p = '.';
                    113:                                        *++p = CNULL;
1.4       millert   114:                                }
1.1       dm        115:                        } else {
                    116:                                /*
                    117:                                 * Couldn't find anything, so give up.
                    118:                                 */
                    119:                                debugmsg(DM_MISC, "Cannot find dir of `%s'",
                    120:                                         pathname);
1.5       millert   121:                                return(NULL);
1.1       dm        122:                        }
                    123:                        continue;
                    124:                } else {
                    125:                        error("%s: lstat failed: %s", pathname, SYSERR);
1.5       millert   126:                        return(NULL);
1.1       dm        127:                }
                    128:        }
                    129:
                    130:        if (statbuf)
                    131:                bcopy((char *) &filestat, (char *) statbuf, sizeof(filestat));
                    132:
                    133:        /*
                    134:         * Trim the "/." that we added.
                    135:         */
                    136:        p = &file[strlen(file) - 1];
                    137:        if (*p == '.')
                    138:                *p-- = CNULL;
                    139:        for ( ; p && *p && *p == '/' && p != file; --p)
                    140:                *p = CNULL;
                    141:
                    142:        /*
                    143:         * If this file is a symlink we really want the parent directory
                    144:         * name in case the symlink points to another filesystem.
                    145:         */
                    146:        if (S_ISLNK(filestat.st_mode))
                    147:                if ((p = (char *) strrchr(file, '/')) && *p+1) {
                    148:                        /* Is this / (root)? */
                    149:                        if (p == file)
                    150:                                file[1] = CNULL;
                    151:                        else
                    152:                                *p = CNULL;
                    153:                }
                    154:
                    155:        if (strcmp(pathname, file) == 0)
                    156:                *isvalid = 1;
                    157:
1.13      guenther  158:        return(*file ? file : NULL);
1.1       dm        159: }
                    160:
                    161: #if defined(NFS_CHECK) || defined(RO_CHECK)
                    162:
                    163: /*
                    164:  * Find the device that "filest" is on in the "mntinfo" linked list.
                    165:  */
1.9       millert   166: mntent_t *
                    167: findmnt(struct stat *filest, struct mntinfo *mntinfo)
1.1       dm        168: {
1.7       mpech     169:        struct mntinfo *mi;
1.1       dm        170:
                    171:        for (mi = mntinfo; mi; mi = mi->mi_nxt) {
                    172:                if (mi->mi_mnt->me_flags & MEFLAG_IGNORE)
                    173:                        continue;
                    174:                if (filest->st_dev == mi->mi_statb->st_dev)
                    175:                        return(mi->mi_mnt);
                    176:        }
                    177:
1.5       millert   178:        return(NULL);
1.1       dm        179: }
                    180:
                    181: /*
                    182:  * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
                    183:  */
1.9       millert   184: int
                    185: isdupmnt(mntent_t *mnt, struct mntinfo *mntinfo)
1.1       dm        186: {
1.7       mpech     187:        struct mntinfo *m;
1.1       dm        188:
                    189:        for (m = mntinfo; m; m = m->mi_nxt)
                    190:                if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0)
                    191:                        return(1);
                    192:
                    193:        return(0);
                    194: }
                    195:
                    196: /*
                    197:  * Alarm clock
                    198:  */
1.9       millert   199: void
                    200: wakeup(int dummy)
1.1       dm        201: {
                    202:        debugmsg(DM_CALL, "wakeup() in filesys.c called");
                    203:        longjmp(env, 1);
                    204: }
                    205:
                    206: /*
                    207:  * Make a linked list of mntinfo structures.
                    208:  * Use "mi" as the base of the list if it's non NULL.
                    209:  */
1.9       millert   210: struct mntinfo *
                    211: makemntinfo(struct mntinfo *mi)
1.1       dm        212: {
1.6       millert   213:        static struct mntinfo *mntinfo;
                    214:        struct mntinfo *newmi, *m;
1.1       dm        215:        struct stat mntstat;
                    216:        mntent_t *mnt;
                    217:        int timeo = 310;
                    218:
1.15    ! guenther  219:        if (setmountent()) {
        !           220:                message(MT_NERROR, "setmntent failed: %s", SYSERR);
1.5       millert   221:                return(NULL);
1.1       dm        222:        }
                    223:
                    224:        (void) signal(SIGALRM, wakeup);
                    225:        (void) alarm(timeo);
                    226:        if (setjmp(env)) {
                    227:                message(MT_NERROR, "Timeout getting mount info");
1.5       millert   228:                return(NULL);
1.1       dm        229:        }
                    230:
                    231:        mntinfo = mi;
1.15    ! guenther  232:        while ((mnt = getmountent()) != NULL) {
1.1       dm        233:                debugmsg(DM_MISC, "mountent = '%s' (%s)",
                    234:                         mnt->me_path, mnt->me_type);
                    235:
                    236:                /*
                    237:                 * Make sure we don't already have it for some reason
                    238:                 */
                    239:                if (isdupmnt(mnt, mntinfo))
                    240:                        continue;
                    241:
                    242:                /*
                    243:                 * Get stat info
                    244:                 */
                    245:                if (stat(mnt->me_path, &mntstat) != 0) {
                    246:                        message(MT_WARNING, "%s: Cannot stat filesystem: %s",
                    247:                                mnt->me_path, SYSERR);
                    248:                        continue;
                    249:                }
                    250:
                    251:                /*
                    252:                 * Create new entry
                    253:                 */
                    254:                newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo));
                    255:                newmi->mi_mnt = newmountent(mnt);
                    256:                newmi->mi_statb =
                    257:                    (struct stat *) xcalloc(1, sizeof(struct stat));
                    258:                bcopy((char *) &mntstat, (char *) newmi->mi_statb,
                    259:                      sizeof(struct stat));
                    260:
                    261:                /*
                    262:                 * Add entry to list
                    263:                 */
                    264:                if (mntinfo) {
1.9       millert   265:                        for (m = mntinfo; m->mi_nxt; m = m->mi_nxt)
                    266:                                continue;
1.1       dm        267:                        m->mi_nxt = newmi;
                    268:                } else
                    269:                        mntinfo = newmi;
                    270:        }
                    271:
1.15    ! guenther  272:        alarm(0);
        !           273:        endmountent();
1.1       dm        274:
                    275:        return(mntinfo);
                    276: }
                    277:
                    278: /*
                    279:  * Given a name like /usr/src/etc/foo.c returns the mntent
                    280:  * structure for the file system it lives in.
                    281:  *
                    282:  * If "statbuf" is not NULL it is used as the stat buffer too avoid
                    283:  * stat()'ing the file again back in server.c.
                    284:  */
1.9       millert   285: mntent_t *
                    286: getmntpt(char *pathname, struct stat *statbuf, int *isvalid)
1.1       dm        287: {
                    288:        static struct mntinfo *mntinfo = NULL;
                    289:        static struct stat filestat;
                    290:        struct stat *pstat;
                    291:        struct mntinfo *tmpmi;
1.7       mpech     292:        mntent_t *mnt;
1.1       dm        293:
                    294:        /*
                    295:         * Use the supplied stat buffer if not NULL or our own.
                    296:         */
                    297:        if (statbuf)
                    298:                pstat = statbuf;
                    299:        else
                    300:                pstat = &filestat;
                    301:
                    302:        if (!find_file(pathname, pstat, isvalid))
1.5       millert   303:                return(NULL);
1.1       dm        304:
                    305:        /*
                    306:         * Make mntinfo if it doesn't exist.
                    307:         */
                    308:        if (!mntinfo)
1.5       millert   309:                mntinfo = makemntinfo(NULL);
1.1       dm        310:
                    311:        /*
                    312:         * Find the mnt that pathname is on.
                    313:         */
1.9       millert   314:        if ((mnt = findmnt(pstat, mntinfo)) != NULL)
1.1       dm        315:                return(mnt);
                    316:
                    317:        /*
                    318:         * We failed to find correct mnt, so maybe it's a newly
                    319:         * mounted filesystem.  We rebuild mntinfo and try again.
                    320:         */
1.9       millert   321:        if ((tmpmi = makemntinfo(mntinfo)) != NULL) {
1.1       dm        322:                mntinfo = tmpmi;
1.9       millert   323:                if ((mnt = findmnt(pstat, mntinfo)) != NULL)
1.1       dm        324:                        return(mnt);
                    325:        }
                    326:
                    327:        error("%s: Could not find mount point", pathname);
1.5       millert   328:        return(NULL);
1.1       dm        329: }
                    330:
                    331: #endif /* NFS_CHECK || RO_CHECK */
                    332:
                    333: #if    defined(NFS_CHECK)
                    334: /*
                    335:  * Is "path" NFS mounted?  Return 1 if it is, 0 if not, or -1 on error.
                    336:  */
1.9       millert   337: int
                    338: is_nfs_mounted(char *path, struct stat *statbuf, int *isvalid)
1.1       dm        339: {
                    340:        mntent_t *mnt;
                    341:
                    342:        if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
                    343:                return(-1);
                    344:
1.2       dm        345:        /*
                    346:         * We treat "cachefs" just like NFS
                    347:         */
                    348:        if ((strcmp(mnt->me_type, METYPE_NFS) == 0) ||
                    349:            (strcmp(mnt->me_type, "cachefs") == 0))
1.1       dm        350:                return(1);
                    351:
                    352:        return(0);
                    353: }
                    354: #endif /* NFS_CHECK */
                    355:
                    356: #if    defined(RO_CHECK)
                    357: /*
                    358:  * Is "path" on a read-only mounted filesystem?
                    359:  * Return 1 if it is, 0 if not, or -1 on error.
                    360:  */
1.9       millert   361: int
                    362: is_ro_mounted(char *path, struct stat *statbuf, int *isvalid)
1.1       dm        363: {
                    364:        mntent_t *mnt;
                    365:
                    366:        if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
                    367:                return(-1);
                    368:
                    369:        if (mnt->me_flags & MEFLAG_READONLY)
                    370:                return(1);
                    371:
                    372:        return(0);
                    373: }
                    374: #endif /* RO_CHECK */
                    375:
                    376: /*
                    377:  * Is "path" a symlink?
                    378:  * Return 1 if it is, 0 if not, or -1 on error.
                    379:  */
1.9       millert   380: int
                    381: is_symlinked(char *path, struct stat *statbuf, int *isvalid)
1.1       dm        382: {
                    383:        static struct stat stb;
                    384:
                    385:        if (!(*isvalid)) {
                    386:                if (lstat(path, &stb) != 0)
                    387:                        return(-1);
                    388:                statbuf = &stb;
                    389:        }
                    390:
                    391:        if (S_ISLNK(statbuf->st_mode))
                    392:                return(1);
                    393:
                    394:        return(0);
                    395: }
                    396:
                    397: /*
                    398:  * Get filesystem information for "file".  Set freespace
                    399:  * to the amount of free (available) space and number of free
                    400:  * files (inodes) on the filesystem "file" resides on.
                    401:  * Returns 0 on success or -1 on failure.
                    402:  * Filesystem values < 0 indicate unsupported or unavailable
                    403:  * information.
                    404:  */
1.9       millert   405: int
1.12      krw       406: getfilesysinfo(char *file, int64_t *freespace, int64_t *freefiles)
1.1       dm        407: {
1.14      guenther  408:        struct statfs statfsbuf;
1.1       dm        409:        char *mntpt;
1.14      guenther  410:        int64_t val;
1.1       dm        411:        int t, r;
                    412:
                    413:        /*
                    414:         * Get the mount point of the file.
                    415:         */
                    416:        mntpt = find_file(file, NULL, &t);
                    417:        if (!mntpt) {
                    418:                debugmsg(DM_MISC, "unknown mount point for `%s'", file);
                    419:                return(-1);
                    420:        }
                    421:
                    422:        r = statfs(mntpt, &statfsbuf);
                    423:        if (r < 0) {
                    424:                error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR);
                    425:                return(-1);
                    426:        }
                    427:
                    428:        /*
                    429:         * If values are < 0, then assume the value is unsupported
                    430:         * or unavailable for that filesystem type.
                    431:         */
1.14      guenther  432:        val = -1;
1.1       dm        433:        if (statfsbuf.f_bavail >= 0)
1.14      guenther  434:                val = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) / 2;
                    435:        *freespace = val;
1.1       dm        436:
1.14      guenther  437:        val = -1;
1.12      krw       438:        if (statfsbuf.f_favail >= 0)
1.14      guenther  439:                val = statfsbuf.f_favail;
                    440:        *freefiles = val;
1.1       dm        441:
                    442:        return(0);
                    443: }