| /* |
| * Copyright (c) 2000-2001 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <xfs/xfs.h> |
| #include <xfs/jdm.h> |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <dirent.h> |
| #include <time.h> |
| #include <assert.h> |
| #include <string.h> |
| |
| #include "config.h" |
| |
| #include "types.h" |
| #include "util.h" |
| #include "openutil.h" |
| #include "mlog.h" |
| #include "global.h" |
| #include "drive.h" |
| #include "media.h" |
| #include "content.h" |
| #include "content_inode.h" |
| #include "inomap.h" |
| #include "mmap.h" |
| #include "arch_xlate.h" |
| |
| /* structure definitions used locally ****************************************/ |
| |
| /* restores the inomap into a file |
| */ |
| #define PERS_NAME "inomap" |
| |
| /* reserve the first page for persistent state |
| */ |
| struct pers { |
| size64_t hnkcnt; |
| /* number of hunks in map |
| */ |
| size64_t segcnt; |
| /* number of segments |
| */ |
| xfs_ino_t last_ino_added; |
| }; |
| |
| typedef struct pers pers_t; |
| |
| #define PERSSZ perssz |
| |
| /* declarations of externally defined global symbols *************************/ |
| |
| extern size_t pgsz; |
| |
| |
| /* forward declarations of locally defined static functions ******************/ |
| |
| /* inomap primitives |
| */ |
| static int map_getset(xfs_ino_t, int, bool_t); |
| static int map_set(xfs_ino_t ino, int); |
| static seg_t * map_getsegment(xfs_ino_t ino); |
| |
| /* definition of locally defined global variables ****************************/ |
| |
| |
| /* definition of locally defined static variables *****************************/ |
| |
| static int pers_fd = -1; |
| /* file descriptor for persistent inomap backing store |
| */ |
| |
| /* context for inomap construction - initialized by inomap_restore_pers |
| */ |
| static uint64_t hnkcnt; |
| static uint64_t segcnt; |
| static hnk_t *roothnkp = 0; |
| static hnk_t *tailhnkp; |
| static seg_t *lastsegp; |
| static xfs_ino_t last_ino_added; |
| |
| /* map context and operators |
| */ |
| |
| static inline void |
| SEG_SET_BITS(seg_t *segp, xfs_ino_t ino, int state) |
| { |
| register xfs_ino_t relino; |
| register uint64_t mask; |
| register uint64_t clrmask; |
| relino = ino - segp->base; |
| mask = (uint64_t)1 << relino; |
| clrmask = ~mask; |
| switch(state) { |
| case 0: |
| segp->lobits &= clrmask; |
| segp->mebits &= clrmask; |
| segp->hibits &= clrmask; |
| break; |
| case 1: |
| segp->lobits |= mask; |
| segp->mebits &= clrmask; |
| segp->hibits &= clrmask; |
| break; |
| case 2: |
| segp->lobits &= clrmask; |
| segp->mebits |= mask; |
| segp->hibits &= clrmask; |
| break; |
| case 3: |
| segp->lobits |= mask; |
| segp->mebits |= mask; |
| segp->hibits &= clrmask; |
| break; |
| case 4: |
| segp->lobits &= clrmask; |
| segp->mebits &= clrmask; |
| segp->hibits |= mask; |
| break; |
| case 5: |
| segp->lobits |= mask; |
| segp->mebits &= clrmask; |
| segp->hibits |= mask; |
| break; |
| case 6: |
| segp->lobits &= clrmask; |
| segp->mebits |= mask; |
| segp->hibits |= mask; |
| break; |
| case 7: |
| segp->lobits |= mask; |
| segp->mebits |= mask; |
| segp->hibits |= mask; |
| break; |
| } |
| } |
| |
| static inline int |
| SEG_GET_BITS(seg_t *segp, xfs_ino_t ino) |
| { |
| int state; |
| register xfs_ino_t relino; |
| register uint64_t mask; |
| relino = ino - segp->base; |
| mask = (uint64_t)1 << relino; |
| if (segp->lobits & mask) { |
| state = 1; |
| } else { |
| state = 0; |
| } |
| if (segp->mebits & mask) { |
| state |= 2; |
| } |
| if (segp->hibits & mask) { |
| state |= 4; |
| } |
| |
| return state; |
| } |
| |
| /* definition of locally defined global functions ****************************/ |
| |
| rv_t |
| inomap_restore_pers(drive_t *drivep, |
| content_inode_hdr_t *scrhdrp, |
| char *hkdir) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| char *perspath; |
| pers_t *persp; |
| hnk_t *pershnkp; |
| hnk_t *tmphnkp; |
| int fd; |
| /* REFERENCED */ |
| int nread; |
| int rval; |
| /* REFERENCED */ |
| int rval1; |
| int i; |
| bool_t ok; |
| |
| /* sanity checks |
| */ |
| assert(INOPERSEG == (sizeof(((seg_t *)0)->lobits) * NBBY)); |
| assert(sizeof(hnk_t) == HNKSZ); |
| assert(sizeof(pers_t) <= PERSSZ); |
| |
| /* get inomap info from media hdr |
| */ |
| hnkcnt = scrhdrp->cih_inomap_hnkcnt; |
| segcnt = scrhdrp->cih_inomap_segcnt; |
| last_ino_added = scrhdrp->cih_inomap_lastino; |
| |
| /* truncate and open the backing store |
| */ |
| perspath = open_pathalloc(hkdir, PERS_NAME, 0); |
| (void)unlink(perspath); |
| fd = open(perspath, |
| O_RDWR | O_CREAT, |
| S_IRUSR | S_IWUSR); |
| if (fd < 0) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, _( |
| "could not open %s: %s\n"), |
| perspath, |
| strerror(errno)); |
| return RV_ERROR; |
| } |
| |
| /* mmap the persistent hdr and space for the map |
| */ |
| persp = (pers_t *) mmap_autogrow( |
| PERSSZ |
| + |
| sizeof(hnk_t) * (size_t)hnkcnt, |
| fd, |
| (off64_t)0); |
| if (persp == (pers_t *)-1) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, _( |
| "unable to map %s: %s\n"), |
| perspath, |
| strerror(errno)); |
| return RV_ERROR; |
| } |
| |
| /* load the pers hdr |
| */ |
| persp->hnkcnt = hnkcnt; |
| persp->segcnt = segcnt; |
| persp->last_ino_added = last_ino_added; |
| |
| tmphnkp = (hnk_t *)calloc((size_t)hnkcnt, sizeof(hnk_t)); |
| assert(tmphnkp); |
| |
| /* read the map in from media |
| */ |
| nread = read_buf((char *)tmphnkp, |
| sizeof(hnk_t) * (size_t)hnkcnt, |
| (void *)drivep, |
| (rfp_t)dop->do_read, |
| (rrbfp_t)dop->do_return_read_buf, |
| &rval); |
| |
| pershnkp = (hnk_t *)((char *)persp + PERSSZ); |
| for(i = 0; i < hnkcnt; i++) { |
| xlate_hnk(&tmphnkp[i], &pershnkp[i], 1); |
| } |
| free(tmphnkp); |
| |
| mlog(MLOG_NITTY, "inomap_restore_pers: pre-munmap\n"); |
| |
| /* close up |
| */ |
| rval1 = munmap((void *)persp, |
| PERSSZ |
| + |
| sizeof(hnk_t) * (size_t)hnkcnt); |
| assert(! rval1); |
| (void)close(fd); |
| free((void *)perspath); |
| |
| mlog(MLOG_NITTY, "inomap_restore_pers: post-munmap\n"); |
| |
| /* check the return code from read |
| */ |
| switch(rval) { |
| case 0: |
| assert((size_t)nread == sizeof(hnk_t) * (size_t)hnkcnt); |
| ok = inomap_sync_pers(hkdir); |
| if (! ok) { |
| return RV_ERROR; |
| } |
| return RV_OK; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| } |
| |
| /* peels inomap from media |
| */ |
| rv_t |
| inomap_discard(drive_t *drivep, content_inode_hdr_t *scrhdrp) |
| { |
| drive_ops_t *dop = drivep->d_opsp; |
| uint64_t tmphnkcnt; |
| /* REFERENCED */ |
| int nread; |
| int rval; |
| |
| /* get inomap info from media hdr |
| */ |
| tmphnkcnt = scrhdrp->cih_inomap_hnkcnt; |
| |
| /* read the map in from media |
| */ |
| nread = read_buf(0, |
| sizeof(hnk_t) * (size_t)tmphnkcnt, |
| (void *)drivep, |
| (rfp_t)dop->do_read, |
| (rrbfp_t)dop->do_return_read_buf, |
| &rval); |
| /* check the return code from read |
| */ |
| switch(rval) { |
| case 0: |
| assert((size_t)nread == sizeof(hnk_t) * (size_t)hnkcnt); |
| return RV_OK; |
| case DRIVE_ERROR_EOD: |
| case DRIVE_ERROR_EOF: |
| case DRIVE_ERROR_EOM: |
| case DRIVE_ERROR_MEDIA: |
| case DRIVE_ERROR_CORRUPTION: |
| return RV_CORRUPT; |
| case DRIVE_ERROR_DEVICE: |
| return RV_DRIVE; |
| case DRIVE_ERROR_CORE: |
| default: |
| return RV_CORE; |
| } |
| } |
| |
| bool_t |
| inomap_sync_pers(char *hkdir) |
| { |
| char *perspath; |
| pers_t *persp; |
| hnk_t *hnkp; |
| |
| /* sanity checks |
| */ |
| assert(sizeof(hnk_t) == HNKSZ); |
| |
| /* only needed once per session |
| */ |
| if (pers_fd >= 0) { |
| return BOOL_TRUE; |
| } |
| |
| /* open the backing store. if not present, ok, hasn't been created yet |
| */ |
| perspath = open_pathalloc(hkdir, PERS_NAME, 0); |
| pers_fd = open(perspath, O_RDWR); |
| if (pers_fd < 0) { |
| return BOOL_TRUE; |
| } |
| |
| /* mmap the persistent hdr |
| */ |
| persp = (pers_t *) mmap_autogrow( |
| PERSSZ, |
| pers_fd, |
| (off64_t)0); |
| if (persp == (pers_t *)-1) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, _( |
| "unable to map %s hdr: %s\n"), |
| perspath, |
| strerror(errno)); |
| return BOOL_FALSE; |
| } |
| |
| /* read the pers hdr |
| */ |
| hnkcnt = persp->hnkcnt; |
| segcnt = persp->segcnt; |
| last_ino_added = persp->last_ino_added; |
| |
| /* mmap the pers inomap |
| */ |
| assert(hnkcnt * sizeof(hnk_t) <= (size64_t)INT32MAX); |
| roothnkp = (hnk_t *) mmap_autogrow( |
| sizeof(hnk_t) * (size_t)hnkcnt, |
| pers_fd, |
| (off64_t)PERSSZ); |
| if (roothnkp == (hnk_t *)-1) { |
| mlog(MLOG_NORMAL | MLOG_ERROR, _( |
| "unable to map %s: %s\n"), |
| perspath, |
| strerror(errno)); |
| return BOOL_FALSE; |
| } |
| |
| /* correct the next pointers |
| */ |
| for (hnkp = roothnkp |
| ; |
| hnkp < roothnkp + (int)hnkcnt - 1 |
| ; |
| hnkp++) { |
| hnkp->nextp = hnkp + 1; |
| } |
| hnkp->nextp = 0; |
| |
| /* calculate the tail pointers |
| */ |
| tailhnkp = hnkp; |
| assert(hnkcnt > 0); |
| lastsegp = &tailhnkp->seg[(int)(segcnt |
| - |
| SEGPERHNK * (hnkcnt - 1) |
| - |
| 1)]; |
| |
| /* now all inomap operators will work |
| */ |
| return BOOL_TRUE; |
| } |
| |
| /* de-allocate the persistent inomap |
| */ |
| void |
| inomap_del_pers(char *hkdir) |
| { |
| char *perspath = open_pathalloc(hkdir, PERS_NAME, 0); |
| (void)unlink(perspath); |
| free((void *)perspath); |
| } |
| |
| /* mark all included non-dirs as MAP_NDR_NOREST |
| */ |
| void |
| inomap_sanitize(void) |
| { |
| hnk_t *hnkp; |
| seg_t *segp; |
| |
| /* step through all hunks, segs, and inos |
| */ |
| for (hnkp = roothnkp |
| ; |
| hnkp != 0 |
| ; |
| hnkp = hnkp->nextp) { |
| for (segp = hnkp->seg |
| ; |
| segp < hnkp->seg + SEGPERHNK |
| ; |
| segp++) { |
| xfs_ino_t ino; |
| if (hnkp == tailhnkp && segp > lastsegp) { |
| return; |
| } |
| for (ino = segp->base |
| ; |
| ino < segp->base + INOPERSEG |
| ; |
| ino++) { |
| int state; |
| if (ino > last_ino_added) { |
| return; |
| } |
| state = SEG_GET_BITS(segp, ino); |
| if (state == MAP_NDR_CHANGE) { |
| state = MAP_NDR_NOREST; |
| SEG_SET_BITS(segp, ino, state); |
| } |
| } |
| } |
| } |
| } |
| |
| /* called to mark a non-dir ino as TO be restored |
| */ |
| void |
| inomap_rst_add(xfs_ino_t ino) |
| { |
| assert(pers_fd >= 0); |
| (void)map_set(ino, MAP_NDR_CHANGE); |
| } |
| |
| /* called to mark a non-dir ino as NOT to be restored |
| */ |
| void |
| inomap_rst_del(xfs_ino_t ino) |
| { |
| assert(pers_fd >= 0); |
| (void)map_set(ino, MAP_NDR_NOREST); |
| } |
| |
| /* called to ask if any inos in the given range need to be restored. |
| * range is inclusive |
| */ |
| bool_t |
| inomap_rst_needed(xfs_ino_t firstino, xfs_ino_t lastino) |
| { |
| hnk_t *hnkp; |
| seg_t *segp; |
| |
| /* if inomap not restored/resynced, just say yes |
| */ |
| if (! roothnkp) { |
| return BOOL_TRUE; |
| } |
| |
| /* may be completely out of range |
| */ |
| if (firstino > last_ino_added) { |
| return BOOL_FALSE; |
| } |
| |
| /* find the hunk/seg containing first ino or any ino beyond |
| */ |
| for (hnkp = roothnkp ; hnkp != 0 ; hnkp = hnkp->nextp) { |
| if (firstino > hnkp->maxino) { |
| continue; |
| } |
| for (segp = hnkp->seg; segp < hnkp->seg + SEGPERHNK ; segp++){ |
| if (hnkp == tailhnkp && segp > lastsegp) { |
| return BOOL_FALSE; |
| } |
| if (firstino < segp->base + INOPERSEG) { |
| goto begin; |
| } |
| } |
| } |
| return BOOL_FALSE; |
| |
| begin: |
| /* search until at least one ino is needed or until beyond last ino |
| */ |
| for (; ;) { |
| xfs_ino_t ino; |
| |
| if (segp->base > lastino) { |
| return BOOL_FALSE; |
| } |
| for (ino = segp->base ; ino < segp->base + INOPERSEG ; ino++){ |
| int state; |
| if (ino < firstino) { |
| continue; |
| } |
| if (ino > lastino) { |
| return BOOL_FALSE; |
| } |
| state = SEG_GET_BITS(segp, ino); |
| if (state == MAP_NDR_CHANGE) { |
| return BOOL_TRUE; |
| } |
| } |
| segp++; |
| if (hnkp == tailhnkp && segp > lastsegp) { |
| return BOOL_FALSE; |
| } |
| if (segp >= hnkp->seg + SEGPERHNK) { |
| hnkp = hnkp->nextp; |
| if (! hnkp) { |
| return BOOL_FALSE; |
| } |
| segp = hnkp->seg; |
| } |
| } |
| /* NOTREACHED */ |
| } |
| |
| /* calls the callback for all inos with an inomap state included |
| * in the state mask. stops iteration when inos exhaused or cb |
| * returns FALSE. |
| */ |
| void |
| inomap_cbiter(int statemask, |
| bool_t (* cbfunc)(void *ctxp, xfs_ino_t ino), |
| void *ctxp) |
| { |
| hnk_t *hnkp; |
| seg_t *segp; |
| |
| /* step through all hunks, segs, and inos |
| */ |
| for (hnkp = roothnkp |
| ; |
| hnkp != 0 |
| ; |
| hnkp = hnkp->nextp) { |
| for (segp = hnkp->seg |
| ; |
| segp < hnkp->seg + SEGPERHNK |
| ; |
| segp++) { |
| xfs_ino_t ino; |
| if (hnkp == tailhnkp && segp > lastsegp) { |
| return; |
| } |
| for (ino = segp->base |
| ; |
| ino < segp->base + INOPERSEG |
| ; |
| ino++) { |
| int state; |
| if (ino > last_ino_added) { |
| return; |
| } |
| state = SEG_GET_BITS(segp, ino); |
| if (statemask & (1 << state)) { |
| bool_t ok; |
| ok = (cbfunc)(ctxp, ino); |
| if (! ok) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /* definition of locally defined static functions ****************************/ |
| |
| /* map_getset - locates and gets the state of the specified ino, |
| * and optionally sets the state to a new value. |
| */ |
| static int |
| map_getset(xfs_ino_t ino, int newstate, bool_t setflag) |
| { |
| int state; |
| seg_t *segp; |
| |
| if ((segp = map_getsegment(ino)) == NULL) { |
| return MAP_INO_UNUSED; |
| } |
| |
| state = SEG_GET_BITS(segp, ino); |
| if (setflag) { |
| SEG_SET_BITS(segp, ino, newstate); |
| } |
| return state; |
| } |
| |
| static seg_t * |
| map_getsegment(xfs_ino_t ino) |
| { |
| uint64_t min; |
| uint64_t max; |
| uint64_t hnk; |
| uint64_t seg; |
| |
| /* Use binary search to find the hunk that contains the inode number, |
| * if any. This counts on the fact that all the hunks are contiguous |
| * in memory and therefore can be treated as an array instead of a |
| * list. |
| */ |
| |
| min = 0; |
| max = hnkcnt - 1; |
| |
| while (max >= min) { |
| hnk = min + ((max - min) / 2); |
| |
| if (roothnkp[hnk].seg[0].base > ino) { |
| max = hnk - 1; /* in a lower hunk */ |
| } else if (roothnkp[hnk].maxino < ino) { |
| min = hnk + 1; /* in a higher hunk */ |
| } else { |
| break; /* we have a winner! */ |
| } |
| } |
| |
| if (max < min) { |
| return NULL; /* inode number fell between hunks */ |
| } |
| |
| /* Use binary search to find the segment within the hunk that contains |
| * the inode number, if any. |
| */ |
| |
| min = 0; |
| if (hnk == hnkcnt - 1) { |
| max = segcnt - SEGPERHNK * (hnkcnt - 1) - 1; |
| } else { |
| max = SEGPERHNK - 1; |
| } |
| |
| while (max >= min) { |
| seg = min + ((max - min) / 2); |
| |
| if (roothnkp[hnk].seg[seg].base > ino) { |
| max = seg - 1; /* in a lower segment */ |
| } else if (roothnkp[hnk].seg[seg].base + INOPERSEG <= ino) { |
| min = seg + 1; /* in a higher segment */ |
| } else { |
| return &roothnkp[hnk].seg[seg]; /* we have a winner! */ |
| } |
| } |
| return NULL; |
| } |
| |
| static int |
| map_set(xfs_ino_t ino, int state) |
| { |
| int oldstate; |
| |
| oldstate = map_getset(ino, state, BOOL_TRUE); |
| return oldstate; |
| } |