[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.18

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