proc: prevent accessing /proc/<PID>/environ until it's ready
[deliverable/linux.git] / lib / vsprintf.c
index f44e178e6edec22e2f7c405f8cb433b2be935f83..ccb664b54280755779237e213a2c3c8fe1021c89 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/blkdev.h>
 #endif
 
+#include "../mm/internal.h"    /* For the trace_print_flags arrays */
+
 #include <asm/page.h>          /* for PAGE_SIZE */
 #include <asm/sections.h>      /* for dereference_function_descriptor() */
 #include <asm/byteorder.h>     /* cpu_to_le16 */
@@ -1407,6 +1409,72 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
        }
 }
 
+static
+char *format_flags(char *buf, char *end, unsigned long flags,
+                                       const struct trace_print_flags *names)
+{
+       unsigned long mask;
+       const struct printf_spec strspec = {
+               .field_width = -1,
+               .precision = -1,
+       };
+       const struct printf_spec numspec = {
+               .flags = SPECIAL|SMALL,
+               .field_width = -1,
+               .precision = -1,
+               .base = 16,
+       };
+
+       for ( ; flags && names->name; names++) {
+               mask = names->mask;
+               if ((flags & mask) != mask)
+                       continue;
+
+               buf = string(buf, end, names->name, strspec);
+
+               flags &= ~mask;
+               if (flags) {
+                       if (buf < end)
+                               *buf = '|';
+                       buf++;
+               }
+       }
+
+       if (flags)
+               buf = number(buf, end, flags, numspec);
+
+       return buf;
+}
+
+static noinline_for_stack
+char *flags_string(char *buf, char *end, void *flags_ptr, const char *fmt)
+{
+       unsigned long flags;
+       const struct trace_print_flags *names;
+
+       switch (fmt[1]) {
+       case 'p':
+               flags = *(unsigned long *)flags_ptr;
+               /* Remove zone id */
+               flags &= (1UL << NR_PAGEFLAGS) - 1;
+               names = pageflag_names;
+               break;
+       case 'v':
+               flags = *(unsigned long *)flags_ptr;
+               names = vmaflag_names;
+               break;
+       case 'g':
+               flags = *(gfp_t *)flags_ptr;
+               names = gfpflag_names;
+               break;
+       default:
+               WARN_ONCE(1, "Unsupported flags modifier: %c\n", fmt[1]);
+               return buf;
+       }
+
+       return format_flags(buf, end, flags, names);
+}
+
 int kptr_restrict __read_mostly;
 
 /*
@@ -1495,6 +1563,11 @@ int kptr_restrict __read_mostly;
  * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
  *        (legacy clock framework) of the clock
  * - 'Cr' For a clock, it prints the current rate of the clock
+ * - 'G' For flags to be printed as a collection of symbolic strings that would
+ *       construct the specific value. Supported flags given by option:
+ *       p page flags (see struct page) given as pointer to unsigned long
+ *       g gfp flags (GFP_* and __GFP_*) given as pointer to gfp_t
+ *       v vma flags (VM_*) given as pointer to unsigned long
  *
  * ** Please update also Documentation/printk-formats.txt when making changes **
  *
@@ -1648,6 +1721,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
                return bdev_name(buf, end, ptr, spec, fmt);
 #endif
 
+       case 'G':
+               return flags_string(buf, end, ptr, fmt);
        }
        spec.flags |= SMALL;
        if (spec.field_width == -1) {
@@ -2565,8 +2640,12 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
                if (*fmt == '*') {
                        if (!*str)
                                break;
-                       while (!isspace(*fmt) && *fmt != '%' && *fmt)
+                       while (!isspace(*fmt) && *fmt != '%' && *fmt) {
+                               /* '%*[' not yet supported, invalid format */
+                               if (*fmt == '[')
+                                       return num;
                                fmt++;
+                       }
                        while (!isspace(*str) && *str)
                                str++;
                        continue;
@@ -2639,6 +2718,59 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
                        num++;
                }
                continue;
+               /*
+                * Warning: This implementation of the '[' conversion specifier
+                * deviates from its glibc counterpart in the following ways:
+                * (1) It does NOT support ranges i.e. '-' is NOT a special
+                *     character
+                * (2) It cannot match the closing bracket ']' itself
+                * (3) A field width is required
+                * (4) '%*[' (discard matching input) is currently not supported
+                *
+                * Example usage:
+                * ret = sscanf("00:0a:95","%2[^:]:%2[^:]:%2[^:]",
+                *              buf1, buf2, buf3);
+                * if (ret < 3)
+                *    // etc..
+                */
+               case '[':
+               {
+                       char *s = (char *)va_arg(args, char *);
+                       DECLARE_BITMAP(set, 256) = {0};
+                       unsigned int len = 0;
+                       bool negate = (*fmt == '^');
+
+                       /* field width is required */
+                       if (field_width == -1)
+                               return num;
+
+                       if (negate)
+                               ++fmt;
+
+                       for ( ; *fmt && *fmt != ']'; ++fmt, ++len)
+                               set_bit((u8)*fmt, set);
+
+                       /* no ']' or no character set found */
+                       if (!*fmt || !len)
+                               return num;
+                       ++fmt;
+
+                       if (negate) {
+                               bitmap_complement(set, set, 256);
+                               /* exclude null '\0' byte */
+                               clear_bit(0, set);
+                       }
+
+                       /* match must be non-empty */
+                       if (!test_bit((u8)*str, set))
+                               return num;
+
+                       while (test_bit((u8)*str, set) && field_width--)
+                               *s++ = *str++;
+                       *s = '\0';
+                       ++num;
+               }
+               continue;
                case 'o':
                        base = 8;
                        break;
This page took 0.02527 seconds and 5 git commands to generate.