Add -R to chmod

Change-Id: I30f976598bd937f656a3ad3395349f09e695cfce
diff --git a/toolbox/chmod.c b/toolbox/chmod.c
index 31a53bf..2a524e9 100644
--- a/toolbox/chmod.c
+++ b/toolbox/chmod.c
@@ -4,17 +4,74 @@
 #include <sys/types.h>
 #include <dirent.h>
 #include <errno.h>
+#include <sys/limits.h>
+#include <sys/stat.h>
 
 #include <unistd.h>
 #include <time.h>
 
+void recurse_chmod(char* path, int mode)
+{
+    struct dirent *dp;
+    DIR *dir = opendir(path);
+    if (dir == NULL) {
+        // not a directory, carry on
+        return;
+    }
+    char *subpath = malloc(sizeof(char)*PATH_MAX);
+    int pathlen = strlen(path);
+
+    while ((dp = readdir(dir)) != NULL) {
+        if (strcmp(dp->d_name, ".") == 0 ||
+            strcmp(dp->d_name, "..") == 0) continue;
+
+        if (strlen(dp->d_name) + pathlen + 2/*NUL and slash*/ > PATH_MAX) {
+            fprintf(stderr, "Invalid path specified: too long\n");
+            exit(1);
+        }
+
+        strcpy(subpath, path);
+        strcat(subpath, "/");
+        strcat(subpath, dp->d_name);
+
+        if (chmod(subpath, mode) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", subpath, strerror(errno));
+            exit(1);
+        }
+
+        recurse_chmod(subpath, mode);
+    }
+    free(subpath);
+    closedir(dir);
+}
+
+static int usage()
+{
+    fprintf(stderr, "Usage: chmod [OPTION] <MODE> <FILE>\n");
+    fprintf(stderr, "  -R, --recursive         change files and directories recursively\n");
+    fprintf(stderr, "  --help                  display this help and exit\n");
+
+    return 10;
+}
+
 int chmod_main(int argc, char **argv)
 {
     int i;
 
-    if (argc < 3) {
-        fprintf(stderr, "Usage: chmod <MODE> <FILE>\n");
-        return 10;
+    if (argc < 3 || strcmp(argv[1], "--help") == 0) {
+        return usage();
+    }
+
+    int recursive = (strcmp(argv[1], "-R") == 0 ||
+                     strcmp(argv[1], "--recursive") == 0) ? 1 : 0;
+
+    if (recursive && argc < 4) {
+        return usage();
+    }
+
+    if (recursive) {
+        argc--;
+        argv++;
     }
 
     int mode = 0;
@@ -29,11 +86,15 @@
         }
         s++;
     }
+
     for (i = 2; i < argc; i++) {
         if (chmod(argv[i], mode) < 0) {
             fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
             return 10;
         }
+        if (recursive) {
+            recurse_chmod(argv[i], mode);
+        }
     }
     return 0;
 }