module: support reading coresize from /sys if supported

Linux 3.3 introduced the coresize attribute in /sys/module/*. When
available, use this instead of parsing some portion of /proc/modules.
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index fb3a64e..1271c70 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -1679,22 +1679,43 @@
  * kmod_module_get_size:
  * @mod: kmod module
  *
- * Get the size of this kmod module as returned by Linux kernel. It reads the
- * file /proc/modules to search for this module and get its size.
+ * Get the size of this kmod module as returned by Linux kernel. If supported,
+ * the size is read from the coresize attribute in /sys/module. For older
+ * kernels, this falls back on /proc/modules and searches for the specified
+ * module to get its size.
  *
  * Returns: the size of this kmod module.
  */
 KMOD_EXPORT long kmod_module_get_size(const struct kmod_module *mod)
 {
-	// FIXME TODO: this should be available from /sys/module/foo
 	FILE *fp;
 	char line[4096];
 	int lineno = 0;
 	long size = -ENOENT;
+	int dfd, cfd;
 
 	if (mod == NULL)
 		return -ENOENT;
 
+	/* try to open the module dir in /sys. If this fails, don't
+	 * bother trying to find the size as we know the module isn't
+	 * loaded.
+	 */
+	snprintf(line, sizeof(line), "/sys/module/%s", mod->name);
+	dfd = open(line, O_RDONLY);
+	if (dfd < 0)
+		return -errno;
+
+	/* available as of linux 3.3.x */
+	cfd = openat(dfd, "coresize", O_RDONLY|O_CLOEXEC);
+	if (cfd >= 0) {
+		if (read_str_long(cfd, &size, 10) < 0)
+			ERR(mod->ctx, "failed to read coresize from %s\n", line);
+		close(cfd);
+		goto done;
+	}
+
+	/* fall back on parsing /proc/modules */
 	fp = fopen("/proc/modules", "re");
 	if (fp == NULL) {
 		int err = -errno;
@@ -1729,6 +1750,9 @@
 		break;
 	}
 	fclose(fp);
+
+done:
+	close(dfd);
 	return size;
 }