LCOV - code coverage report
Current view: top level - home/snorch/criu - irmap.c (source / functions) Hit Total Coverage
Test: coverage3.info Lines: 45 169 26.6 %
Date: 2014-04-22 Functions: 4 10 40.0 %
Branches: 19 110 17.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * IRMAP -- inode reverse mapping.
       3                 :            :  *
       4                 :            :  * Helps us to map inode number (and device) back to path
       5                 :            :  * so that we can restore inotify/fanotify-s.
       6                 :            :  *
       7                 :            :  * Scanning _is_ slow, so we limit it with hints, which are
       8                 :            :  * heurisitical known places where notifies are typically put.
       9                 :            :  */
      10                 :            : 
      11                 :            : #include <stdbool.h>
      12                 :            : #include <fcntl.h>
      13                 :            : #include <dirent.h>
      14                 :            : #include <string.h>
      15                 :            : #include <stdio.h>
      16                 :            : #include <sys/stat.h>
      17                 :            : #include <unistd.h>
      18                 :            : 
      19                 :            : #include "xmalloc.h"
      20                 :            : #include "irmap.h"
      21                 :            : #include "mount.h"
      22                 :            : #include "log.h"
      23                 :            : #include "util.h"
      24                 :            : #include "image.h"
      25                 :            : #include "stats.h"
      26                 :            : #include "pstree.h"
      27                 :            : 
      28                 :            : #include "protobuf.h"
      29                 :            : #include "protobuf/fsnotify.pb-c.h"
      30                 :            : #include "protobuf/fh.pb-c.h"
      31                 :            : 
      32                 :            : #undef  LOG_PREFIX
      33                 :            : #define LOG_PREFIX "irmap: "
      34                 :            : 
      35                 :            : #define IRMAP_CACHE_BITS        5
      36                 :            : #define IRMAP_CACHE_SIZE        (1 << IRMAP_CACHE_BITS)
      37                 :            : #define IRMAP_CACHE_MASK        (IRMAP_CACHE_SIZE - 1)
      38                 :            : 
      39                 :            : static inline int irmap_hashfn(unsigned int s_dev, unsigned long i_ino)
      40                 :            : {
      41                 :          0 :         return (s_dev + i_ino) & IRMAP_CACHE_MASK;
      42                 :            : }
      43                 :            : 
      44                 :            : struct irmap {
      45                 :            :         unsigned int dev;
      46                 :            :         unsigned long ino;
      47                 :            :         char *path;
      48                 :            :         struct irmap *next;
      49                 :            :         bool revalidate;
      50                 :            :         int nr_kids;
      51                 :            :         struct irmap *kids;
      52                 :            : };
      53                 :            : 
      54                 :            : static struct irmap *cache[IRMAP_CACHE_SIZE];
      55                 :            : 
      56                 :            : static struct irmap hints[] = {
      57                 :            :         { .path = "/etc", .nr_kids = -1, },
      58                 :            :         { .path = "/var/spool", .nr_kids = -1, },
      59                 :            :         { .path = "/lib/udev", .nr_kids = -1, },
      60                 :            :         { .path = "/no-such-path", .nr_kids = -1, },
      61                 :            :         { },
      62                 :            : };
      63                 :            : 
      64                 :            : /*
      65                 :            :  * Update inode (and device) number and cache the entry
      66                 :            :  */
      67                 :          0 : static int irmap_update_stat(struct irmap *i)
      68                 :            : {
      69                 :            :         struct stat st;
      70                 :            :         int mntns_root;
      71                 :            :         unsigned hv;
      72                 :            : 
      73         [ #  # ]:          0 :         if (i->ino)
      74                 :            :                 return 0;
      75                 :            : 
      76                 :          0 :         mntns_root = get_service_fd(ROOT_FD_OFF);
      77                 :            : 
      78                 :          0 :         pr_debug("Refresh stat for %s\n", i->path);
      79         [ #  # ]:          0 :         if (fstatat(mntns_root, i->path + 1, &st, AT_SYMLINK_NOFOLLOW)) {
      80                 :          0 :                 pr_perror("Can't stat %s", i->path);
      81                 :          0 :                 return -1;
      82                 :            :         }
      83                 :            : 
      84                 :          0 :         i->revalidate = false;
      85                 :          0 :         i->dev = st.st_dev;
      86                 :          0 :         i->ino = st.st_ino;
      87         [ #  # ]:          0 :         if (!S_ISDIR(st.st_mode))
      88                 :          0 :                 i->nr_kids = 0; /* don't irmap_update_dir */
      89                 :            : 
      90                 :          0 :         hv = irmap_hashfn(i->dev, i->ino);
      91                 :          0 :         i->next = cache[hv];
      92                 :          0 :         cache[hv] = i;
      93                 :            : 
      94                 :          0 :         return 0;
      95                 :            : }
      96                 :            : 
      97                 :            : /*
      98                 :            :  * Update list of children, but don't cache any. Later
      99                 :            :  * we'll scan them one-by-one and cache.
     100                 :            :  */
     101                 :          0 : static int irmap_update_dir(struct irmap *t)
     102                 :            : {
     103                 :            :         int fd, nr = 0, dlen, mntns_root;
     104                 :            :         DIR *dfd;
     105                 :            :         struct dirent *de;
     106                 :            : 
     107         [ #  # ]:          0 :         if (t->nr_kids >= 0)
     108                 :            :                 return 0;
     109                 :            : 
     110                 :          0 :         mntns_root = get_service_fd(ROOT_FD_OFF);
     111                 :            : 
     112                 :          0 :         pr_debug("Refilling %s dir\n", t->path);
     113                 :          0 :         fd = openat(mntns_root, t->path + 1, O_RDONLY);
     114         [ #  # ]:          0 :         if (fd < 0) {
     115                 :          0 :                 pr_perror("Can't open %s", t->path);
     116                 :          0 :                 return -1;
     117                 :            :         }
     118                 :            : 
     119                 :          0 :         dlen = strlen(t->path);
     120                 :          0 :         dfd = fdopendir(fd);
     121         [ #  # ]:          0 :         if (!dfd) {
     122                 :          0 :                 pr_perror("Can't opendir %s", t->path);
     123                 :          0 :                 return -1;
     124                 :            :         }
     125                 :            : 
     126                 :          0 :         errno = 0;
     127         [ #  # ]:          0 :         while ((de = readdir(dfd)) != NULL) {
     128                 :            :                 struct irmap *k;
     129                 :            : 
     130         [ #  # ]:          0 :                 if (dir_dots(de))
     131                 :          0 :                         continue;
     132                 :            : 
     133                 :          0 :                 nr++;
     134 [ #  # ][ #  # ]:          0 :                 if (xrealloc_safe(&t->kids, nr * sizeof(struct irmap)))
                 [ #  # ]
     135                 :            :                         goto out_err;
     136                 :            : 
     137                 :          0 :                 k = &t->kids[nr - 1];
     138                 :            : 
     139                 :          0 :                 k->kids = NULL;       /* for xrealloc above */
     140                 :          0 :                 k->ino = 0;   /* for irmap_update_stat */
     141                 :          0 :                 k->nr_kids = -1; /* for irmap_update_dir */
     142                 :            : 
     143         [ #  # ]:          0 :                 k->path = xmalloc(dlen + strlen(de->d_name) + 2);
     144         [ #  # ]:          0 :                 if (!k->path)
     145                 :            :                         goto out_err;
     146                 :            : 
     147                 :          0 :                 sprintf(k->path, "%s/%s", t->path, de->d_name);
     148                 :            :         }
     149                 :            : 
     150         [ #  # ]:          0 :         if (errno) {
     151                 :          0 :                 pr_perror("Readdir failed");
     152                 :          0 :                 goto out_err;
     153                 :            :         }
     154                 :            : 
     155                 :          0 :         closedir(dfd);
     156                 :          0 :         close(fd);
     157                 :          0 :         t->nr_kids = nr;
     158                 :          0 :         return 0;
     159                 :            : 
     160                 :            : out_err:
     161         [ #  # ]:          0 :         xfree(t->kids);
     162                 :          0 :         closedir(dfd);
     163                 :          0 :         close(fd);
     164                 :          0 :         return -1;
     165                 :            : }
     166                 :            : 
     167                 :          0 : static struct irmap *irmap_scan(struct irmap *t, unsigned int dev, unsigned long ino)
     168                 :            : {
     169                 :            :         struct irmap *c;
     170                 :            :         int i;
     171                 :            : 
     172         [ #  # ]:          0 :         if (irmap_update_stat(t))
     173                 :            :                 return NULL;
     174                 :            : 
     175 [ #  # ][ #  # ]:          0 :         if (t->dev == dev && t->ino == ino)
     176                 :            :                 return t;
     177                 :            : 
     178         [ #  # ]:          0 :         if (irmap_update_dir(t))
     179                 :            :                 return NULL;
     180                 :            : 
     181         [ #  # ]:          0 :         for (i = 0; i < t->nr_kids; i++) {
     182                 :          0 :                 c = irmap_scan(&t->kids[i], dev, ino);
     183         [ #  # ]:          0 :                 if (c)
     184                 :            :                         return c;
     185                 :            :         }
     186                 :            : 
     187                 :            :         return NULL;
     188                 :            : }
     189                 :            : 
     190                 :          0 : static int irmap_revalidate(struct irmap *c, struct irmap **p)
     191                 :            : {
     192                 :            :         struct stat st;
     193                 :            :         int mntns_root;
     194                 :            : 
     195                 :          0 :         mntns_root = get_service_fd(ROOT_FD_OFF);
     196                 :            : 
     197                 :          0 :         pr_debug("Revalidate stat for %s\n", c->path);
     198         [ #  # ]:          0 :         if (fstatat(mntns_root, c->path + 1, &st, AT_SYMLINK_NOFOLLOW)) {
     199                 :            :                 /* File can be (re)moved, so just treat it as invalid */
     200                 :          0 :                 pr_perror("Can't stat %s", c->path);
     201                 :          0 :                 goto invalid;
     202                 :            :         }
     203                 :            : 
     204         [ #  # ]:          0 :         if (c->dev != st.st_dev)
     205                 :            :                 goto invalid;
     206         [ #  # ]:          0 :         if (c->ino != st.st_ino)
     207                 :            :                 goto invalid;
     208                 :            : 
     209                 :          0 :         c->revalidate = false;
     210                 :          0 :         return 0;
     211                 :            : 
     212                 :            : invalid:
     213                 :          0 :         pr_debug("\t%x:%lx is invalid\n", c->dev, c->ino);
     214                 :          0 :         *p = c->next;
     215         [ #  # ]:          0 :         xfree(c->path);
     216         [ #  # ]:          0 :         xfree(c);
     217                 :            :         return 1;
     218                 :            : }
     219                 :            : 
     220                 :          0 : char *irmap_lookup(unsigned int s_dev, unsigned long i_ino)
     221                 :            : {
     222                 :            :         struct irmap *c, *h, **p;
     223                 :            :         char *path = NULL;
     224                 :            :         int hv;
     225                 :            : 
     226                 :            :         s_dev = kdev_to_odev(s_dev);
     227                 :            : 
     228                 :          0 :         pr_debug("Resolving %x:%lx path\n", s_dev, i_ino);
     229                 :            : 
     230         [ #  # ]:          0 :         if (mntns_collect_root(root_item->pid.real) < 0)
     231                 :            :                 goto out;
     232                 :            : 
     233                 :          0 :         timing_start(TIME_IRMAP_RESOLVE);
     234                 :            : 
     235                 :            :         hv = irmap_hashfn(s_dev, i_ino);
     236         [ #  # ]:          0 :         for (p = &cache[hv]; *p; p = &(*p)->next) {
     237                 :            :                 c = *p;
     238 [ #  # ][ #  # ]:          0 :                 if (!(c->dev == s_dev && c->ino == i_ino))
     239                 :          0 :                         continue;
     240                 :            : 
     241 [ #  # ][ #  # ]:          0 :                 if (c->revalidate && irmap_revalidate(c, p))
     242                 :          0 :                         continue;
     243                 :            : 
     244                 :          0 :                 pr_debug("\tFound %s in cache\n", c->path);
     245                 :          0 :                 path = c->path;
     246                 :          0 :                 goto out;
     247                 :            :         }
     248                 :            : 
     249         [ #  # ]:          0 :         for (h = hints; h->path; h++) {
     250                 :          0 :                 pr_debug("Scanning %s hint\n", h->path);
     251                 :          0 :                 c = irmap_scan(h, s_dev, i_ino);
     252         [ #  # ]:          0 :                 if (c) {
     253                 :          0 :                         pr_debug("\tScanned %s\n", c->path);
     254                 :          0 :                         path = c->path;
     255                 :          0 :                         goto out;
     256                 :            :                 }
     257                 :            :         }
     258                 :            : 
     259                 :            : out:
     260                 :          0 :         timing_stop(TIME_IRMAP_RESOLVE);
     261                 :          0 :         return path;
     262                 :            : }
     263                 :            : 
     264                 :            : /*
     265                 :            :  * IRMAP pre-cache -- do early irmap scan on pre-dump to reduce
     266                 :            :  * the freeze time on dump
     267                 :            :  */
     268                 :            : 
     269                 :            : struct irmap_predump {
     270                 :            :         unsigned int dev;
     271                 :            :         unsigned long ino;
     272                 :            :         FhEntry fh;
     273                 :            :         struct irmap_predump *next;
     274                 :            : };
     275                 :            : 
     276                 :            : static struct irmap_predump *predump_queue;
     277                 :            : 
     278                 :         36 : int irmap_queue_cache(unsigned int dev, unsigned long ino,
     279                 :            :                 FhEntry *fh)
     280                 :            : {
     281                 :            :         struct irmap_predump *ip;
     282                 :            : 
     283         [ -  + ]:         36 :         ip = xmalloc(sizeof(*ip));
     284         [ +  - ]:         36 :         if (!ip)
     285                 :            :                 return -1;
     286                 :            : 
     287                 :         36 :         ip->dev = dev;
     288                 :         36 :         ip->ino = ino;
     289                 :         36 :         ip->fh = *fh;
     290                 :         36 :         fh->handle = NULL; /* don't free in free_fhandle */
     291                 :            : 
     292                 :         36 :         pr_debug("Queue %x:%lx for pre-dump\n", dev, ino);
     293                 :            : 
     294                 :         36 :         ip->next = predump_queue;
     295                 :         36 :         predump_queue = ip;
     296                 :         36 :         return 0;
     297                 :            : }
     298                 :            : 
     299                 :       1344 : int irmap_predump_run(void)
     300                 :            : {
     301                 :            :         int ret = 0, fd;
     302                 :            :         struct irmap_predump *ip;
     303                 :            : 
     304                 :       1344 :         fd = open_image_at(AT_FDCWD, CR_FD_IRMAP_CACHE, O_DUMP);
     305         [ +  - ]:       1344 :         if (fd < 0)
     306                 :            :                 return -1;
     307                 :            : 
     308                 :       1344 :         pr_info("Running irmap pre-dump\n");
     309                 :            : 
     310         [ +  + ]:       1380 :         for (ip = predump_queue; ip; ip = ip->next) {
     311                 :         36 :                 pr_debug("\tchecking %x:%lx\n", ip->dev, ip->ino);
     312                 :         36 :                 ret = check_open_handle(ip->dev, ip->ino, &ip->fh);
     313         [ -  + ]:         36 :                 if (ret) {
     314                 :          0 :                         pr_err("Failed to resolve %x:%lx\n", ip->dev, ip->ino);
     315                 :          0 :                         break;
     316                 :            :                 }
     317                 :            : 
     318         [ -  + ]:         36 :                 if (ip->fh.path) {
     319                 :          0 :                         IrmapCacheEntry ic = IRMAP_CACHE_ENTRY__INIT;
     320                 :            : 
     321                 :          0 :                         pr_info("Irmap cache %x:%lx -> %s\n", ip->dev, ip->ino, ip->fh.path);
     322                 :          0 :                         ic.dev = ip->dev;
     323                 :          0 :                         ic.inode = ip->ino;
     324                 :          0 :                         ic.path = ip->fh.path;
     325                 :            : 
     326                 :          0 :                         ret = pb_write_one(fd, &ic, PB_IRMAP_CACHE);
     327         [ #  # ]:          0 :                         if (ret)
     328                 :            :                                 break;
     329                 :            :                 }
     330                 :            :         }
     331                 :            : 
     332                 :       1344 :         close(fd);
     333                 :       1344 :         return ret;
     334                 :            : }
     335                 :            : 
     336                 :          0 : static int irmap_cache_one(IrmapCacheEntry *ie)
     337                 :            : {
     338                 :            :         struct irmap *ic;
     339                 :            :         unsigned hv;
     340                 :            : 
     341         [ #  # ]:          0 :         ic = xmalloc(sizeof(*ic));
     342         [ #  # ]:          0 :         if (!ic)
     343                 :            :                 return -1;
     344                 :            : 
     345                 :          0 :         ic->dev = ie->dev;
     346                 :          0 :         ic->ino = ie->inode;
     347         [ #  # ]:          0 :         ic->path = xstrdup(ie->path);
     348         [ #  # ]:          0 :         if (!ie->path) {
     349         [ #  # ]:          0 :                 xfree(ic);
     350                 :            :                 return -1;
     351                 :            :         }
     352                 :            : 
     353                 :          0 :         ic->nr_kids = 0;
     354                 :            :         /*
     355                 :            :          * We've loaded entry from cache, thus we'll need to check
     356                 :            :          * whether it's still valid when find it in cache.
     357                 :            :          */
     358                 :          0 :         ic->revalidate = true;
     359                 :            : 
     360                 :          0 :         pr_debug("Pre-cache %x:%lx -> %s\n", ic->dev, ic->ino, ic->path);
     361                 :            : 
     362                 :          0 :         hv = irmap_hashfn(ic->dev, ic->ino);
     363                 :          0 :         ic->next = cache[hv];
     364                 :          0 :         cache[hv] = ic;
     365                 :            : 
     366                 :          0 :         return 0;
     367                 :            : }
     368                 :            : 
     369                 :       1792 : static int open_irmap_cache(int *fd)
     370                 :            : {
     371                 :            :         int dir = AT_FDCWD;
     372                 :            : 
     373                 :       1792 :         pr_info("Searching irmap cache in work dir\n");
     374                 :            : in:
     375                 :       3136 :         *fd = open_image_at(dir, CR_FD_IRMAP_CACHE, O_RSTR | O_OPT);
     376         [ +  + ]:       3136 :         if (dir != AT_FDCWD)
     377                 :       1344 :                 close(dir);
     378                 :            : 
     379         [ +  + ]:       3136 :         if (*fd >= 0) {
     380                 :       1344 :                 pr_info("... done\n");
     381                 :       1344 :                 return 1;
     382                 :            :         }
     383                 :            : 
     384 [ +  - ][ +  - ]:       1792 :         if (*fd == -ENOENT && dir == AT_FDCWD) {
     385                 :       1792 :                 pr_info("Searching irmap cache in parent\n");
     386                 :       1792 :                 dir = openat(get_service_fd(IMG_FD_OFF), CR_PARENT_LINK, O_RDONLY);
     387         [ +  + ]:       1792 :                 if (dir >= 0)
     388                 :            :                         goto in;
     389                 :            :         }
     390                 :            : 
     391         [ +  - ]:        448 :         if (*fd != -ENOENT)
     392                 :            :                 return -1;
     393                 :            : 
     394                 :        448 :         pr_info("No irmap cache\n");
     395                 :        448 :         return 0;
     396                 :            : }
     397                 :            : 
     398                 :       1792 : int irmap_load_cache(void)
     399                 :            : {
     400                 :            :         int fd, ret;
     401                 :            : 
     402                 :       1792 :         ret = open_irmap_cache(&fd);
     403         [ +  + ]:       1792 :         if (ret <= 0)
     404                 :            :                 return ret;
     405                 :            : 
     406                 :       1344 :         pr_info("Loading irmap cache\n");
     407                 :            :         while (1) {
     408                 :            :                 IrmapCacheEntry *ic;
     409                 :            : 
     410                 :       1344 :                 ret = pb_read_one_eof(fd, &ic, PB_IRMAP_CACHE);
     411         [ -  + ]:       1344 :                 if (ret <= 0)
     412                 :            :                         break;
     413                 :            : 
     414                 :          0 :                 ret = irmap_cache_one(ic);
     415         [ #  # ]:          0 :                 if (ret < 0)
     416                 :            :                         break;
     417                 :            : 
     418                 :          0 :                 irmap_cache_entry__free_unpacked(ic, NULL);
     419                 :          0 :         }
     420                 :            : 
     421                 :       1344 :         close(fd);
     422                 :       1344 :         return ret;
     423                 :            : }

Generated by: LCOV version 1.9