1 /* $OpenBSD: filesys.c,v 1.19 2015/01/21 04:08:37 guenther Exp $ */ 2 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. 15 * 3. Neither the name of the University nor the names of its contributors 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 32 #include <sys/types.h> 33 #include <sys/mount.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "server.h" 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 */ 52 char * 53 find_file(char *pathname, struct stat *statbuf, int *isvalid) 54 { 55 static char last_pathname[PATH_MAX]; 56 static char file[PATH_MAX + 3]; 57 static struct stat filestat; 58 char *p; 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 79 if (strlen(pathname) > sizeof(file) + 3) { 80 error("%s: Name to large for buffer.", pathname); 81 return(NULL); 82 } 83 84 /* 85 * Save for next time 86 */ 87 (void) strlcpy(last_pathname, pathname, sizeof(last_pathname)); 88 89 if (*pathname == '/') 90 (void) strlcpy(file, pathname, sizeof(file)); 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 */ 97 (void) strlcpy(file, "./", sizeof(file)); 98 (void) strlcat(file, pathname, sizeof(file)); 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 */ 111 if ((p = strrchr(file, '/')) != NULL) { 112 if (strcmp(p, "/.") == 0) { 113 *p = CNULL; 114 } else { 115 *++p = '.'; 116 *++p = CNULL; 117 } 118 } else { 119 /* 120 * Couldn't find anything, so give up. 121 */ 122 debugmsg(DM_MISC, "Cannot find dir of `%s'", 123 pathname); 124 return(NULL); 125 } 126 continue; 127 } else { 128 error("%s: lstat failed: %s", pathname, SYSERR); 129 return(NULL); 130 } 131 } 132 133 if (statbuf) 134 bcopy(&filestat, 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 = 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 161 return(*file ? file : NULL); 162 } 163 164 165 /* 166 * Find the device that "filest" is on in the "mntinfo" linked list. 167 */ 168 mntent_t * 169 findmnt(struct stat *filest, struct mntinfo *mntinfo) 170 { 171 struct mntinfo *mi; 172 173 for (mi = mntinfo; mi; mi = mi->mi_nxt) { 174 if (mi->mi_mnt->me_flags & MEFLAG_IGNORE) 175 continue; 176 if (filest->st_dev == mi->mi_dev) 177 return(mi->mi_mnt); 178 } 179 180 return(NULL); 181 } 182 183 /* 184 * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements? 185 */ 186 int 187 isdupmnt(mntent_t *mnt, struct mntinfo *mntinfo) 188 { 189 struct mntinfo *m; 190 191 for (m = mntinfo; m; m = m->mi_nxt) 192 if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0) 193 return(1); 194 195 return(0); 196 } 197 198 /* 199 * Alarm clock 200 */ 201 void 202 wakeup(int dummy) 203 { 204 debugmsg(DM_CALL, "wakeup() in filesys.c called"); 205 longjmp(env, 1); 206 } 207 208 /* 209 * Make a linked list of mntinfo structures. 210 * Use "mi" as the base of the list if it's non NULL. 211 */ 212 struct mntinfo * 213 makemntinfo(struct mntinfo *mi) 214 { 215 static struct mntinfo *mntinfo; 216 struct mntinfo *newmi, *m; 217 struct stat mntstat; 218 mntent_t *mnt; 219 int timeo = 310; 220 221 if (!setmountent()) { 222 message(MT_NERROR, "setmntent failed: %s", SYSERR); 223 return(NULL); 224 } 225 226 (void) signal(SIGALRM, wakeup); 227 (void) alarm(timeo); 228 if (setjmp(env)) { 229 message(MT_NERROR, "Timeout getting mount info"); 230 return(NULL); 231 } 232 233 mntinfo = mi; 234 while ((mnt = getmountent()) != NULL) { 235 debugmsg(DM_MISC, "mountent = '%s'", mnt->me_path); 236 237 /* 238 * Make sure we don't already have it for some reason 239 */ 240 if (isdupmnt(mnt, mntinfo)) 241 continue; 242 243 /* 244 * Get stat info 245 */ 246 if (stat(mnt->me_path, &mntstat) != 0) { 247 message(MT_WARNING, "%s: Cannot stat filesystem: %s", 248 mnt->me_path, SYSERR); 249 continue; 250 } 251 252 /* 253 * Create new entry 254 */ 255 newmi = xcalloc(1, sizeof(*newmi)); 256 newmi->mi_mnt = newmountent(mnt); 257 newmi->mi_dev = mntstat.st_dev; 258 259 /* 260 * Add entry to list 261 */ 262 if (mntinfo) { 263 for (m = mntinfo; m->mi_nxt; m = m->mi_nxt) 264 continue; 265 m->mi_nxt = newmi; 266 } else 267 mntinfo = newmi; 268 } 269 270 alarm(0); 271 endmountent(); 272 273 return(mntinfo); 274 } 275 276 /* 277 * Given a name like /usr/src/etc/foo.c returns the mntent 278 * structure for the file system it lives in. 279 * 280 * If "statbuf" is not NULL it is used as the stat buffer too avoid 281 * stat()'ing the file again back in server.c. 282 */ 283 mntent_t * 284 getmntpt(char *pathname, struct stat *statbuf, int *isvalid) 285 { 286 static struct mntinfo *mntinfo = NULL; 287 static struct stat filestat; 288 struct stat *pstat; 289 struct mntinfo *tmpmi; 290 mntent_t *mnt; 291 292 /* 293 * Use the supplied stat buffer if not NULL or our own. 294 */ 295 if (statbuf) 296 pstat = statbuf; 297 else 298 pstat = &filestat; 299 300 if (!find_file(pathname, pstat, isvalid)) 301 return(NULL); 302 303 /* 304 * Make mntinfo if it doesn't exist. 305 */ 306 if (!mntinfo) 307 mntinfo = makemntinfo(NULL); 308 309 /* 310 * Find the mnt that pathname is on. 311 */ 312 if ((mnt = findmnt(pstat, mntinfo)) != NULL) 313 return(mnt); 314 315 /* 316 * We failed to find correct mnt, so maybe it's a newly 317 * mounted filesystem. We rebuild mntinfo and try again. 318 */ 319 if ((tmpmi = makemntinfo(mntinfo)) != NULL) { 320 mntinfo = tmpmi; 321 if ((mnt = findmnt(pstat, mntinfo)) != NULL) 322 return(mnt); 323 } 324 325 error("%s: Could not find mount point", pathname); 326 return(NULL); 327 } 328 329 330 /* 331 * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error. 332 */ 333 int 334 is_nfs_mounted(char *path, struct stat *statbuf, int *isvalid) 335 { 336 mntent_t *mnt; 337 338 if ((mnt = getmntpt(path, statbuf, isvalid)) == NULL) 339 return(-1); 340 341 if (mnt->me_flags & MEFLAG_NFS) 342 return(1); 343 344 return(0); 345 } 346 347 /* 348 * Is "path" on a read-only mounted filesystem? 349 * Return 1 if it is, 0 if not, or -1 on error. 350 */ 351 int 352 is_ro_mounted(char *path, struct stat *statbuf, int *isvalid) 353 { 354 mntent_t *mnt; 355 356 if ((mnt = getmntpt(path, statbuf, isvalid)) == NULL) 357 return(-1); 358 359 if (mnt->me_flags & MEFLAG_READONLY) 360 return(1); 361 362 return(0); 363 } 364 365 /* 366 * Is "path" a symlink? 367 * Return 1 if it is, 0 if not, or -1 on error. 368 */ 369 int 370 is_symlinked(char *path, struct stat *statbuf, int *isvalid) 371 { 372 static struct stat stb; 373 374 if (!(*isvalid)) { 375 if (lstat(path, &stb) != 0) 376 return(-1); 377 statbuf = &stb; 378 } 379 380 if (S_ISLNK(statbuf->st_mode)) 381 return(1); 382 383 return(0); 384 } 385 386 /* 387 * Get filesystem information for "file". Set freespace 388 * to the amount of free (available) space and number of free 389 * files (inodes) on the filesystem "file" resides on. 390 * Returns 0 on success or -1 on failure. 391 * Filesystem values < 0 indicate unsupported or unavailable 392 * information. 393 */ 394 int 395 getfilesysinfo(char *file, int64_t *freespace, int64_t *freefiles) 396 { 397 struct statfs statfsbuf; 398 char *mntpt; 399 int64_t val; 400 int t, r; 401 402 /* 403 * Get the mount point of the file. 404 */ 405 mntpt = find_file(file, NULL, &t); 406 if (!mntpt) { 407 debugmsg(DM_MISC, "unknown mount point for `%s'", file); 408 return(-1); 409 } 410 411 r = statfs(mntpt, &statfsbuf); 412 if (r < 0) { 413 error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR); 414 return(-1); 415 } 416 417 /* 418 * If values are < 0, then assume the value is unsupported 419 * or unavailable for that filesystem type. 420 */ 421 val = -1; 422 if (statfsbuf.f_bavail >= 0) 423 val = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) / 2; 424 *freespace = val; 425 426 val = -1; 427 if (statfsbuf.f_favail >= 0) 428 val = statfsbuf.f_favail; 429 *freefiles = val; 430 431 return(0); 432 } 433