Vitruvian Penguin
knoppix

[knoppix:5787] start- stop-daemonのunionfs対応化

Date: Wed, 16 Nov 2005 10:54:19 +0900
User-agent: Mutt/1.5.4i-ja.1
はじめまして。中津 と申します。

start-stop-daemonにて、
unionfs上のコマンドを「--exec <コマンド>」で指定すると
期待通りに動かない問題に対してパッチを作成しましたので公開します。

問題の原因:
 start-stop-daemonは、
 「--exec」で指定されたコマンドのプロセスが存在するか否かを判定するために、
 コマンドファイルのstat(2)で得られる(device,inode)と、
 /proc/<pid>/exe のstat(2)で得られる(device,inode)を比較している。
  
 しかし、unionfs内のコマンドが指定されると以下の理由でプロセスの判定に
 失敗する。
 - unionfs内のファイルは独自の(device,inode)を持つ。
 - /proc/<pid>/exe が参照するファイルはunionfsの解決で得られる
   実ファイルである。
           
対策:
 実行ファイルがunionfs内にある場合は、unionfsの解決で辿り着く可能性
 のある全てのファイルに対して(device,inode)のチェックを行なう。

 unionfs-1.1.1/unionctl.c を参考にして、dpkg-1.13.11用のパッチを作成した。
 ただし、unionfsが入れ子になってる場合には対応してない。
 (とりあえずKNOPPIXの場合は大丈夫)

 以下がパッチです。

--- ここから ------ ここから ------ ここから ------ ここから ---
*** dpkg-1.13.11/utils/start-stop-daemon.c      2005-06-06 13:07:12.000000000 
+0900
--- dpkg-1.13.11.new/utils/start-stop-daemon.c  2005-11-10 16:37:44.000000000 
+0900
***************
*** 142,147 ****
--- 142,150 ----
  static int nicelevel = 0;
  
  static struct stat exec_stat;
+ #if defined(OSLinux) /* for daemon in unionfs */
+ static struct stat **cand_stats = NULL;
+ #endif
  #if defined(OSHURD)
  static struct proc_stat_list *procset = NULL;
  #endif
***************
*** 178,183 ****
--- 181,192 ----
  static int pid_is_exec(pid_t pid, const struct stat *esb);
  #endif
  
+ #if defined(OSLinux) /* for daemon in unionfs */
+ extern int find_union(const char *path, char **options, char **actual_path,
+                     int uniononly);
+ static char **parse_union_opts(char *options);
+ static void create_cand_stats(const char *execname);
+ #endif
  
  #ifdef __GNUC__
  static void fatal(const char *format, ...)
***************
*** 616,622 ****
        sprintf(buf, "/proc/%d/exe", pid);
        if (stat(buf, &sb) != 0)
                return 0;
!       return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino);
  }
  
  
--- 625,644 ----
        sprintf(buf, "/proc/%d/exe", pid);
        if (stat(buf, &sb) != 0)
                return 0;
!       if (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino) {
!               return 1;
!       }
!       else if (cand_stats) {  /* for daemon in unionfs */
!               int i=0;
!               while ( cand_stats[i] ) {
!                       if (sb.st_dev == cand_stats[i]->st_dev &&
!                           sb.st_ino == cand_stats[i]->st_ino) {
!                               return 1;
!                       }
!                       i++;
!               }
!       }
!       return 0;
  }
  
  
***************
*** 1166,1171 ****
--- 1188,1425 ----
        }
  }
  
+ #if defined(OSLinux)  /* for daemon in unionfs */
+ static void
+ create_cand_stats(const char *execname)
+ {
+       char abspath[PATH_MAX];
+       char *path =NULL;
+       char *union_opts;
+       char *union_path;
+       
+       if (realpath(execname, abspath) == NULL) {
+               perror("realpath()");
+               exit(EXIT_FAILURE);
+       }
+       path = abspath;
+       if (strcmp(path, "/") && (path[strlen(path) - 1] == '/')) {
+               path[strlen(path) - 1] = '\0';
+       }
+       find_union(path, &union_opts, &union_path, 1);
+       if ( union_opts != NULL ) {
+               char **union_dirs = parse_union_opts(union_opts);
+ 
+               if (union_dirs) {
+                       char *sub_path = abspath + strlen(union_path);
+                       char cand_path[PATH_MAX];
+                       struct stat tmp_stat;
+                       int n = 0;
+                       int m = 0;
+                       
+                       while (union_dirs[n]) {
+                               strcpy(cand_path, union_dirs[n]);
+                               strcat(cand_path, sub_path);
+                               n++;
+                               if (stat(cand_path, &tmp_stat) == 0 ) {
+                                       struct stat *buf
+                                               = malloc(sizeof(struct stat));
+ 
+                                       memcpy(buf, &tmp_stat,
+                                              sizeof(struct stat));
+                                       cand_stats =
+                                         realloc(cand_stats,
+                                                 sizeof(struct stat*) * m+2);
+                                       cand_stats[m] = buf;
+                                       m++;
+                               }
+                       }
+                       if ( m > 0 ) {
+                               cand_stats[m] = NULL;
+                       }
+               }
+       }
+ 
+ }
+ 
+ /*
+  * This function will take a patch and check it against /proc/mounts to
+  * find its mount point. If uniononly is set  then it will make sure its
+  * a unionf  mount point. This function assumes the both options and 
actual_path
+  * are valid and not null;
+  */
+ int find_union(const char *path, char **options, char **actual_path,
+              int uniononly)
+ {
+       FILE *f = NULL;
+       char *s = NULL;
+       char *s2 = NULL;
+       char *p;
+       char *q;
+       int candidate = 0;
+       int mallocsize = 1024;  /* Just a reasonable starting value. */
+ 
+       retry:
+       if (*options) {
+               free(*options);
+               *options = NULL;
+       }
+ 
+       if (*actual_path) {
+               free(*actual_path);
+               *actual_path = NULL;
+       }
+       if (f) {
+               fclose(f);
+               f = NULL;
+       }
+       s2 = realloc(s, mallocsize);
+       if (!s2) {
+               fprintf(stderr, "realloc(%d): %s\n", mallocsize,
+                       strerror(errno));
+               if (s)
+                       free(s);
+               goto out;
+       }
+       s = s2;
+ 
+       f = fopen("/proc/mounts", "r");
+       if (!f) {
+               fprintf(stderr, "fopen(/proc/mounts): %s\n", strerror(errno));
+               goto out;
+       }
+       while (fgets(s, mallocsize, f)) {
+               int testcan;
+ 
+               /* If we don't have enough information, we should remalloc it. 
*/
+               if (strlen(s) == (mallocsize - 1)) {
+                       mallocsize *= 2;
+                       goto retry;
+               }
+ 
+               p = strchr(s, ' ');
+               if (!p)
+                       continue;
+               p++;
+ 
+               q = strchr(p, ' ');
+               if (!q)
+                       continue;
+               *q++ = '\0';
+ 
+               testcan = strlen(p);
+               if (testcan <= candidate) {
+                       continue;
+               }
+ 
+               if (!strncmp(path, p, testcan)) {
+                       if (*actual_path) {
+                               free(*actual_path);
+                       }
+                       *actual_path = strdup(p);
+                       if (!*actual_path) {
+                               fprintf(stderr, "strdup: %s\n",
+                                       strerror(errno));
+                               goto out;
+                       }
+                       p = strchr(q, ' ');
+                       if (!p)
+                               continue;
+                       *p++ = '\0';
+                       if (uniononly) {
+                               if (strcmp(q, "unionfs")) {
+                                       candidate = 0;
+                                       continue;
+                               }
+                       }
+                       candidate = testcan;
+ 
+                       q = strrchr(p, ' ');
+                       if (!q)
+                               continue;
+                       *q = '\0';
+                       q = strrchr(p, ' ');
+                       if (!q)
+                               continue;
+                       *q = '\0';
+ 
+                       if (*options) {
+                               free(*options);
+                       }
+                       *options = strdup(p);
+                       if (!*options) {
+                               fprintf(stderr, "strdup: %s\n",
+                                       strerror(errno));
+                               goto out;
+                       }
+               }
+       }
+ 
+       out:
+       if (s)
+               free(s);
+       if (f)
+               fclose(f);
+ 
+       if (*options) {
+               return 0;
+       }
+ 
+       errno = -ENOENT;
+       return -1;
+ }
+ 
+ static char **
+ parse_union_opts(char *options)
+ {
+       char **ret = NULL;
+       int n = 0;
+ 
+       char *p;
+       char *q;
+       char *r;
+       char *s;
+       int l;
+ 
+       p = options;
+       do {
+               q = strchr(p, ',');
+               if (q) {
+                       *q++ = '\0';
+               }
+               if (!strncmp(p, "dirs=", strlen("dirs="))) {
+                       r = p + strlen("dirs=");
+                       do {
+                               s = strchr(r, ':');
+                               if (s) {
+                                       *s++ = '\0';
+                               }
+ 
+                               n++;
+                               ret = realloc(ret, sizeof(char *) * (n + 1));
+                               if (!ret) {
+                                       perror("realloc()");
+                                       exit(EXIT_FAILURE);
+                               }
+ 
+                               l = strlen(r);
+                               if (((r[l - 1] == 'o') || (r[l - 1] == 'w'))
+                                   && (r[l - 2] == 'r') && (r[l - 3] == '=')) {
+                                       r[l - 3] = '\0';
+                               }
+                               ret[n - 1] = strdup(r);
+                               ret[n] = NULL;
+ 
+                               r = s;
+                       }
+                       while (r);
+               }
+               p = q;
+       }
+       while (p);
+ 
+       return ret;
+ }
+ #endif /* OSLinux */  /* for daemon in unionfs */
  
  int
  main(int argc, char **argv)
***************
*** 1183,1188 ****
--- 1437,1448 ----
        if (execname && stat(execname, &exec_stat))
                fatal("stat %s: %s", execname, strerror(errno));
  
+ #if defined(OSLinux)  /* for daemon in unionfs */
+       if (execname) {
+               create_cand_stats(execname);
+       }
+ #endif
+ 
        if (userspec && sscanf(userspec, "%d", &user_id) != 1) {
                struct passwd *pw;
--- ここまで ------ ここまで ------ ここまで ------ ここまで ---

-- 
/****************************************************
~ > Toshiaki Nakatsu @ Celestar Lexico-Sciences, Inc.
     E-mail: nakatsu@xxxxxxxxxxxxxxxxx
*****************************************************/
<Prev in Thread] Current Thread [Next in Thread>
  • [knoppix:5787] start- stop-daemonのunionfs対応化, Toshiaki Nakatsu <=