Modultils工具源码分析之insmod篇 (4续)

xftw./modutils-2.4.0/util/xftw.c中。

ftw.c文件的开头有一大段注释,解释了这一族函数的由来。特摘录如下。

Insmod——xftw函数

24    /*

25        modutils requires special processing during the file tree walk

26        of /lib/modules/<version> and any paths that the user specifies.

27        The standard ftw() does a blind walk of all paths and can end

28        up following the build symlink down the kernel source tree.

29        Although nftw() has the option to get more control such as not

30        automatically following symbolic links, even that is not enough

31        for modutils.  The requirements are:

32   

33        Paths must be directories or symlinks to directories.

34   

35        Each directory is read and sorted into alphabetical order

36        before processing.

37   

38        A directory is type 1 iff it was specified on a path statement

39        (either explicit or default) and the directory contains a

40        subdirectory with one of the known names and the directory name

41        does not end with "/kernel".  Otherwise it is type 2.

42   

43        In a type 1 directory, walk the kernel subdirectory if it exists,

44        then the old known names in their historical order then any

45        remaining directory entries in alphabetical order and finally any

46        non-directory entries in alphabetical order.

47   

48        Entries in a type 1 directory are filtered against the "prune"

49        list.  A type 1 directory can contain additional files which

50        are not modules nor symlinks to modules.  The prune list skips

51        known additional files, if a distribution wants to store

52        additional text files in the top level directory they should be

53        added to the prune list.

54   

55        A type 2 directory must contain only modules or symlinks to

56        modules.  They are processed in alphabetical order, without

57        pruning.  Symlinks to directories are an error in type 2

58        directories.

59   

60        The user function is not called for type 1 directories, nor for

61        pruned entries.  It is called for type 2 directories and their

62        contents.  It is also called for any files left in a type 1

63        directory after pruning and processing type 2 subdirectories.

64        The user function never sees symlinks, they are resolved before

65        calling the function.

66   

67        Why have different directory types?  The original file tree

68        walk was not well defined.  Some users specified each directory

69        individually, others just pointed at the top level directory.

70        Either version worked until the "build" symlink was added.  Now

71        users who specify the top level directory end up running the

72        entire kernel source tree looking for modules, not nice.  We

73        cannot just ignore symlinks because pcmcia uses symlinks to

74        modules for backwards compatibility.

75   

76        Type 1 is when a user specifies the top level directory which needs

77        special processing, type 2 is individual subdirectories.  But the

78        only way to tell the difference is by looking at the contents.  The

79        "/kernel" directory introduced in 2.3.12 either contains nothing

80        (old make modules_install) or contains all the kernel modules using

81        the same tree structure as the source.  Because "/kernel" can

82        contain old names but is really a type 2 directory, it is detected

83        as a special case.

84               */

 

这里面提到,一些应用程序会指定具体的路径,一些则只指定最上层的目录,在没有引入build符号链接前(build符号链接是引用内核头文件最可靠的方法,它会自动定位到正确的版本),linux原有的ftwnftw函数都能工作,但是引入build符号链接后,指定最上层目录的应用程序将会遍历整个内核查找模块,在效率上不够好。因此,在这里区分出了2种目录。

一般来说,类型1对应于指定上层目录的情况。因为它包含非模块文件或不指向模块的符号链接,因此需要特殊对待——使用prune list过滤。遍历的次序是:内核子目录、按历史排序的已知的目录、按字母排序的目录、按字母排序的非目录项。类型1的目录遍历时,只有在完成prune list过滤后,才对剩余的文件使用用户提供的函数(类似于ftw中的fn参数)。

类型2的目录仅包含模块或指向模块的符号链接,一般来说,这就包括以/kernel结尾的目录路径。它们按字母顺序被依次处理,而且不使用prune list过滤,并且自始至终使用用户提供的函数进行处理。

对于这些目录的处理,有需要共同遵守的规则:

1)  路径必须指向目录或者是指向目录的符号链接。

2)  在处理前,目录需按字母排序。

现在回过头来看xftwlinuxftw的对应物。首先看看prune_list。它定义在./modutils-2.4.0/util/alias.h中。

Insmod——prune数组

221  /*

222  * This is the list of pre-defined "prune"s,

223  * used to exclude paths from scan of /lib/modules.

224  * /etc/modules.conf can add entries but not remove them.

225  */

226  char *prune[] =

227  {

228  "modules.dep",

229         "modules.generic_string",

230         "modules.pcimap",

231         "modules.isapnpmap",

232         "modules.usbmap",

233         "modules.parportmap",

234         "System.map",

235         ".config",

236         "build",          /* symlink to source tree */

237         "vmlinux",

238         "vmlinuz",

239         "bzImage",

240         "zImage",

241         ".rhkmvtag",          /* wish RedHat had told me before they did this */

242         NULL                   /* marks the end of the list! */

421              };

 

310  /* Only external visible function.  Decide on the type of directory and312     

311  * process accordingly.

312  */

313  int xftw(const char *directory, xftw_func_t funcptr)

314  {

315      struct stat statbuf;

316      int ret, i, j, type;

317      xftw_tree_t *t;

318      struct xftw_dirent *c;

319 

320      verbose("xftw starting at %s ", directory);

321      if (lstat(directory, &statbuf)) {

322         verbose("lstat on %s failed/n", directory);

323         return(0);

324      }

325      if (S_ISLNK(statbuf.st_mode)) {

326         char real[PATH_MAX];

327         verbose("resolving symlink to ");

328         if (!(directory = realpath(directory, real))) {

329                if (errno == ENOENT) {

330                       verbose("%s: does not exist, dangling symlink ignored/n", real);

331                       return(0);

332                }

333                perror("... failed");

334                return(-1);

335         }

336         verbose("%s ", directory);

337         if (lstat(directory, &statbuf)) {

338                error("lstat on %s failed ", directory);

339                perror("");

340                return(-1);

341         }

342     }

343      if (!S_ISDIR(statbuf.st_mode)) {

344         error("%s is not a directory/n", directory);

345                return(-1);

346      }

347      verbose("/n");

348

349     /* All returns after this point must be via cleanup */

350

351      if ((ret = xftw_readdir(directory, 0)))

352         goto cleanup;

353

354      t = tree;   /* depth 0 */

355      type = 2;

356      for (i = 0 ; type == 2 && i < t->used; ++i) {

357         c = t->contents+i;

358         for (j = 0; tbtype[j]; ++j) {

359                if (strcmp(c->name, tbtype[j]) == 0 &&

360                S_ISDIR(c->statbuf.st_mode)) {

361                       const char *p = directory + strlen(directory) - 1;

362                       if (*p == '/')

363                              --p;

364                       if (p - directory >= 6 && strncmp(p-6, "/kernel", 7) == 0)

365                              continue; /* "/kernel" path is a special case, type 2 */

366                       type = 1;   /* known subdirectory */

367                       break;

368                }

369         }

370      }

371

372      if (type == 1) {

373         OPT_LIST *p;

374         /* prune entries in type 1 directories only */

375         for (i = 0 ; i < t->used; ++i) {

376                for (p = prunelist; p->name; ++p) {

377                       c = t->contents+i;

378                       if (strcmp(p->name, c->name) == 0) {

379                              verbose("pruned %s/n", c->name);

380                              *(c->name) = '/0';  /* ignore */

381                       }

382                }

383         }

384         /* run known subdirectories first in historical order, "kernel" is now top of list */

385         for (i = 0 ; i < t->used; ++i) {

386                c = t->contents+i;

387                for (j = 0; tbtype[j]; ++j) {

388                       if (*(c->name) &&

389                              strcmp(c->name, tbtype[j]) == 0 &&

390                              S_ISDIR(c->statbuf.st_mode)) {

391                              if ((ret = xftw_type2(directory, c->name, 1, funcptr)))

392                                     goto cleanup;

393                              *(c->name) = '/0';  /* processed */

394                       }

395                }

396         }

397         /* any other directories left, in alphabetical order */

398         for (i = 0 ; i < t->used; ++i) {

399                c = t->contents+i;

400                if (*(c->name) &&

401                       S_ISDIR(c->statbuf.st_mode)) {

402                       if ((ret = xftw_type2(directory, c->name, 1, funcptr)))

403                              goto cleanup;

404                       *(c->name) = '/0';  /* processed */

405                }

406         }

407         /* anything else is passed to the user function */

408         for (i = 0 ; i < t->used; ++i) {

409                c = t->contents+i;

410                if (*(c->name)) {

411                       verbose("%s found in type 1 directory %s/n", c->name, directory);

412                       if ((ret = xftw_do_name(directory, c->name, &(c->statbuf), funcptr)))

413                              goto cleanup;

414                       *(c->name) = '/0';  /* processed */

415                }

416         }

417      }

418      else {

419         /* type 2 */

420         xftw_free_tree(0);

421         if ((ret = xftw_type2(directory, NULL, 0, funcptr)))

422                goto cleanup;

423      }

424

425      /* amazing, it all worked */

426      ret = 0;

427  cleanup:

428      for (i = 0; i < XFTW_MAXDEPTH; ++i)

429         xftw_free_tree(i);

430      return(ret);

431}

 

读过上面的注释,这个函数就不难理解,首先根据遍历的结果,构造一棵排序的树。这个树的定义也在同一文件。

Insmod——xftw_dirent结构

104  struct xftw_dirent {

105    struct stat statbuf;

106      char *name;

107      char *fullname;

108  };

109

110  #define XFTW_MAXDEPTH 64    /* Maximum directory depth handled */

111

112  typedef struct {

113      struct xftw_dirent *contents;

114      int size;

115      int used;

116  } xftw_tree_t;

117

118  static xftw_tree_t tree[XFTW_MAXDEPTH];

 

tree数组的下标对应相应的路径深度。

312~333行的目的很明显,是将符号链接转为它实际指向的文件或目录,在这里只有目录可以接受。获取了目录的实际路径后,调用xftw_readdir,这个函数在同一文件。

Insmod——xftw_readdir函数

217  /* Read a directory and sort it, ignoring "." and ".." */

218  static int xftw_readdir(const char *directory, int depth)

219  {

220      DIR *d;

221      struct dirent *ent;

222      verbose("xftw_readdir %s/n", directory);

223      if (!(d = opendir(directory))) {

224         perror(directory);

225         return(1);

226      }

227      while ((ent = readdir(d))) {

228         char *name;

229         struct xftw_dirent *f;

230         if (strcmp(ent->d_name, ".") == 0 ||

231                strcmp(ent->d_name, "..") == 0)

232                continue;

233         name = xftw_dir_name(directory, ent->d_name);

234         xftw_add_dirent(depth);

235         f = tree[depth].contents+tree[depth].used-1;

236         f->name = xstrdup(ent->d_name);

237         f->fullname = name;     /* do not free name, it is in use */

238         if (lstat(name, &(f->statbuf))) {

239                perror(name);

240                return(1);

241         }

242      }

243      closedir(d);

244      qsort(tree[depth].contents, tree[depth].used, sizeof(*(tree[0].contents)), &xftw_sortdir);

245      return(0);

246          }

 

直到233行之前的代码都很简单,打开目录,忽略名为“.”和“..”子目录。然后使用xftw_dir_name合成这个子目录的路径。函数xftw_dir_name也在xftw.c中。

Insmod——xftw_dir_name函数

152  /* Concatenate directory name and entry name into one string.

153  * Note: caller must free result or leak.

154  */

155  static char *xftw_dir_name(const char *directory, const char *entry)

156  {

157      int i = strlen(directory);

158      char *name;

159      if (entry)

160         i += strlen(entry);

161      i += 2;

162      name = xmalloc(i);

163      strcpy(name, directory); /* safe, xmalloc */

164      if (*directory && entry)

165         strcat(name, "/");    /* safe, xmalloc */

166      if (entry)

167         strcat(name, entry);       /* safe, xmalloc */

168      return(name);

169          }

 

函数xftw_add_dirent在必要时扩展xftw_tree_tcontent的容量,也在同一文件。

Insmod——xftw_add_dirent函数

135  /* Increment dirents used at this depth, resizing if necessary */

136  static void xftw_add_dirent(int depth)

137  {

138      xftw_tree_t *t = tree+depth;

139      int i, size = t->size;

140      if (++t->used < size)

141                return;

142      size += 10; /* arbitrary increment */

143      t->contents = xrealloc(t->contents, size*sizeof(*(t->contents)));

144      for (i = t->size; i < size; ++i) {

              memset(&(t->contents[i].statbuf), 0, sizeof(t->contents[i].statbuf));

145                t->contents[i].name = NULL;

146                t->contents[i].fullname = NULL;

147      }

148      t->size = size;

149          }

 

回到函数xftw_readdir中,235~241行确认找到的路径有效,并保存于tree中。在遍历结束后,使用xftw_sortdir排序。该函数很简单,在同一文件里。

Insmod——xftw_sortdir函数

211  /* Sort directory entries into alphabetical order */

212  static int xftw_sortdir(const void *a, const void *b)

213  {

214      return(strcmp(((struct xftw_dirent *)a)->name, ((struct xftw_dirent *)b)->name));

213         }

 

xftw_readdir返回时,第一层目录下的子目录全部排序保存在tree[0]中了。然后,在xftw函数的347for循环里,开始检查是否类型1的目录。在这里,我们终于知道那些是所谓已知的目录名和所谓的历史顺序,也就是tbtype数组对应目录名和下标,这个数组在./modutils-2.4.0/util/alias.h。不过里面要除去“kernel”,包含它的目录属于第2类。

 

5     /*

6     * tbpath and tbtype are used to build the complete set of paths for finding

7     * modules, but only when we search for individual directories, they are not

8     * used for [boot] and [toplevel] searches.

9     */

10    static char *tbpath[] =

11    {

12           "/lib/modules",

13           NULL                   /* marks the end of the list! */

14    };

15   

16    char *tbtype[] =

17    {

18           "kernel",        /* as of 2.3.14 this must be first */19   

20           "fs",

21           "net",

22           "scsi",

23           "block",

24           "cdrom",

25           "ipv4",

26           "ipv6",

27           "sound",

28           "fc4",

30           "video",

31           "misc",

32           "pcmcia",

33           "atm",

34           "usb",

35           "ide",

36           "ieee1394",

37           "mtd",

38           NULL                   /* marks the end of the list! */

39             };

 

221  /*

222  * This is the list of pre-defined "prune"s,

223  * used to exclude paths from scan of /lib/modules.

224  * /etc/modules.conf can add entries but not remove them.

225  */

226  char *prune[] =

227  {

228         "modules.dep",

229         "modules.generic_string",

230         "modules.pcimap",

231         "modules.isapnpmap",

232         "modules.usbmap",

233         "modules.parportmap",

234         "System.map",

235         ".config",

236         "build",          /* symlink to source tree */

237         "vmlinux",

238         "vmlinuz",

239         "bzImage",

240         "zImage",

241         ".rhkmvtag",          /* wish RedHat had told me before they did this */

242         NULL                   /* marks the end of the list! */

243  };

 

只要找到一个已知目录,就确定是第1类目录,立即跳出循环。然后在366行,按照注释说明,对照prunelist进行过滤。过滤完成后,376行的for循环首先处理已知目录。函数xftw_type2在同一文件里。

Insmod——xftw_type2函数

248  /* Process a type 2 directory */

249  int xftw_type2(const char *directory, const char *entry, int depth, xftw_func_t funcptr)

250  {

251      int ret, i;

252      xftw_tree_t *t = tree+depth;

253      struct stat statbuf;

254      char *dirname = xftw_dir_name(directory, entry);

255 

256      verbose("type 2 %s/n", dirname);

257      if (depth > XFTW_MAXDEPTH) {

258         error("xftw_type2 exceeded maxdepth/n");

259         ret = 1;

260         goto cleanup;

261      }

262      if ((ret = xftw_readdir(dirname, depth)))

263         goto cleanup;

264

265      t = tree+depth;

266      /* user function sees type 2 directories */

267      if ((ret = lstat(dirname, &statbuf)) ||

268         (ret = xftw_do_name("", dirname, &statbuf, funcptr)))

269         goto cleanup;

270

271      /* user sees all contents of type 2 directory, no pruning */

272      for (i = 0; i < t->used; ++i) {

273         struct xftw_dirent *c = t->contents+i;

274         if (S_ISLNK(c->statbuf.st_mode)) {

275                if (!stat(c->name, &(c->statbuf))) {

276                       if (S_ISDIR(c->statbuf.st_mode)) {

277                              error("symlink to directory is not allowed, %s ignored/n", c->name);

278                              *(c->name) = '/0';  /* ignore it */

279                       }

280                }

281         }

282         if (!*(c->name))

283                continue;

284         if (S_ISDIR(c->statbuf.st_mode)) {

285                /* recursion is the curse of the programming classes */

286                ret = xftw_type2(dirname, c->name, depth+1, funcptr);

287                if (ret)

288                       goto cleanup;

289         }

290         else if ((ret = xftw_do_name(dirname, c->name, &(c->statbuf), funcptr)))

291                goto cleanup;

292         *(c->name) = '/0';  /* processed */

293      }

294

295      ret = 0;

296  cleanup:

297      free(dirname);

298      return(ret);

299          }

 

254行构造一个完整路径。在255行我们可以知道,已知目录在这里是按第2类目录来处理的,根据注释这些目录里都是模块名或者指向模块的符号链接。查找目录层深是有限制的,这里是XFTW_MAXDEPTH,定义为64。只要不超过限制,就通过xftw_do_name函数在dirname路径内来查找目的模块。函数也在同一文件里。

Insmod——xftw_do_name函数

171  /* Call the user function for a directory entry */

172  static int xftw_do_name(const char *directory, const char *entry, struct stat *sb, 173      xftw_func_t funcptr)

173  {

174      int ret = 0;

175      char *name = xftw_dir_name(directory, entry);

176

177      if (S_ISLNK(sb->st_mode)) {

178         char real[PATH_MAX], *newname;

179         verbose("resolving %s symlink to ", name);

180         if (!(newname = realpath(name, real))) {

181                if (errno == ENOENT) {

182                       verbose("%s: does not exist, dangling symlink ignored/n", real);

183                       goto cleanup;

184                }

185                perror("... failed");

186                goto cleanup;

187         }

188         verbose("%s ", newname);

189         if (lstat(newname, sb)) {

190                error("lstat on %s failed ", newname);

191                perror("");

192                goto cleanup;

193         }

194         free(name);

195         name = xstrdup(newname);

196  }

197

198      if (!S_ISREG(sb->st_mode) &&

199         !S_ISDIR(sb->st_mode)) {

200         error("%s is not plain file nor directory/n", name);

201         goto cleanup;

202  }

203 

204      verbose("user function %s/n", name);

205      ret = (*funcptr)(name, sb);

206  cleanup:

207      free(name);

208      return(ret);

209          }

 

函数首先处理符号链接(见177行)。获得真实路径名后,通过传入的函数指针funcptr解析保存所求的路径。这个函数实际上就是config_add。前面已经看过。它通过filter_by_filefilter_by_dir来过滤、查找路径。如果config_add查找成功并且只查找第一个匹配,返回1xftw_type2就结束了。不过在这里如果dirname为目录名,config_add会返回0。如果为普通文件,则对应的tree项为空。

dirname为目录名情况下,继续进入272行的循环,分别处理dirname的子目录。首先确保符号链接不是指向目录。对于子目录递归调用xftw_type2,而对于文件则使用xftw_do_name处理。

回到xftw,注意每处理完一个tree项,该项的name都会被置空,防止重复处理。在389行就开始按字母顺序处理路径了。首先处理子目录,然后在399行处理文件。

如果在一开始的指定路径名里找不到包含已知目录名(除kernel外)的项,就认为是第2类型,直接使用xftw_type2处理。至此xftw完毕。

经过长途跋涉,现在终于返回到config_lstmod中。最终该函数完成在modpath中漫长的查找,在list中带着所期待的路径返回了。回到search_module_path,如果用base查找不到路径,就是用base.o再找一遍,如果还是不行而且允许使用zlib,则用base.o.gz再找。只要找到这些路径,search_module_path只返回第一个路径名(因此,查找的顺序很重要)。

 

你可能感兴趣的:(struct,list,tree,processing,工具,symlink)