(root)/
util-linux-2.39/
libblkid/
src/
encode.c
       1  
       2  /*
       3   * encode.c - string conversion routines (mostly for compatibility with
       4   *            udev/volume_id)
       5   *
       6   * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
       7   * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
       8   *
       9   * This file may be redistributed under the terms of the
      10   * GNU Lesser General Public License.
      11   */
      12  #include <stdio.h>
      13  #include <stdlib.h>
      14  #include <stddef.h>
      15  #include <unistd.h>
      16  #include <errno.h>
      17  #include <string.h>
      18  #include <ctype.h>
      19  
      20  #include "blkidP.h"
      21  #include "strutils.h"
      22  
      23  /**
      24   * SECTION: encode
      25   * @title: Encoding utils
      26   * @short_description: encode strings to safe udev-compatible formats
      27   *
      28   */
      29  
      30  /* count of characters used to encode one unicode char */
      31  static int utf8_encoded_expected_len(const char *str)
      32  {
      33  	unsigned char c = (unsigned char)str[0];
      34  
      35  	if (c < 0x80)
      36  		return 1;
      37  	if ((c & 0xe0) == 0xc0)
      38  		return 2;
      39  	if ((c & 0xf0) == 0xe0)
      40  		return 3;
      41  	if ((c & 0xf8) == 0xf0)
      42  		return 4;
      43  	if ((c & 0xfc) == 0xf8)
      44  		return 5;
      45  	if ((c & 0xfe) == 0xfc)
      46  		return 6;
      47  	return 0;
      48  }
      49  
      50  /* decode one unicode char */
      51  static int utf8_encoded_to_unichar(const char *str)
      52  {
      53  	int unichar;
      54  	int len;
      55  	int i;
      56  
      57  	len = utf8_encoded_expected_len(str);
      58  	switch (len) {
      59  	case 1:
      60  		return (int)str[0];
      61  	case 2:
      62  		unichar = str[0] & 0x1f;
      63  		break;
      64  	case 3:
      65  		unichar = (int)str[0] & 0x0f;
      66  		break;
      67  	case 4:
      68  		unichar = (int)str[0] & 0x07;
      69  		break;
      70  	case 5:
      71  		unichar = (int)str[0] & 0x03;
      72  		break;
      73  	case 6:
      74  		unichar = (int)str[0] & 0x01;
      75  		break;
      76  	default:
      77  		return -1;
      78  	}
      79  
      80  	for (i = 1; i < len; i++) {
      81  		if (((int)str[i] & 0xc0) != 0x80)
      82  			return -1;
      83  		unichar <<= 6;
      84  		unichar |= (int)str[i] & 0x3f;
      85  	}
      86  
      87  	return unichar;
      88  }
      89  
      90  /* expected size used to encode one unicode char */
      91  static int utf8_unichar_to_encoded_len(int unichar)
      92  {
      93  	if (unichar < 0x80)
      94  		return 1;
      95  	if (unichar < 0x800)
      96  		return 2;
      97  	if (unichar < 0x10000)
      98  		return 3;
      99  	if (unichar < 0x200000)
     100  		return 4;
     101  	if (unichar < 0x4000000)
     102  		return 5;
     103  	return 6;
     104  }
     105  
     106  /* check if unicode char has a valid numeric range */
     107  static int utf8_unichar_valid_range(int unichar)
     108  {
     109  	if (unichar > 0x10ffff)
     110  		return 0;
     111  	if ((unichar & 0xfffff800) == 0xd800)
     112  		return 0;
     113  	if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
     114  		return 0;
     115  	if ((unichar & 0xffff) == 0xffff)
     116  		return 0;
     117  	return 1;
     118  }
     119  
     120  /* validate one encoded unicode char and return its length */
     121  static int utf8_encoded_valid_unichar(const char *str)
     122  {
     123  	int len;
     124  	int unichar;
     125  	int i;
     126  
     127  	len = utf8_encoded_expected_len(str);
     128  	if (len == 0)
     129  		return -1;
     130  
     131  	/* ascii is valid */
     132  	if (len == 1)
     133  		return 1;
     134  
     135  	/* check if expected encoded chars are available */
     136  	for (i = 0; i < len; i++)
     137  		if ((str[i] & 0x80) != 0x80)
     138  			return -1;
     139  
     140  	unichar = utf8_encoded_to_unichar(str);
     141  
     142  	/* check if encoded length matches encoded value */
     143  	if (utf8_unichar_to_encoded_len(unichar) != len)
     144  		return -1;
     145  
     146  	/* check if value has valid range */
     147  	if (!utf8_unichar_valid_range(unichar))
     148  		return -1;
     149  
     150  	return len;
     151  }
     152  
     153  static int is_whitelisted(char c, const char *white)
     154  {
     155  	if ((c >= '0' && c <= '9') ||
     156  	    (c >= 'A' && c <= 'Z') ||
     157  	    (c >= 'a' && c <= 'z') ||
     158  	    strchr("#+-.:=@_", c) != NULL ||
     159  	    (white != NULL && strchr(white, c) != NULL))
     160  		return 1;
     161  	return 0;
     162  }
     163  
     164  /**
     165   * blkid_encode_string:
     166   * @str: input string to be encoded
     167   * @str_enc: output string to store the encoded input string
     168   * @len: maximum size of the output string, which may be
     169   *       four times as long as the input string
     170   *
     171   * Encode all potentially unsafe characters of a string to the
     172   * corresponding hex value prefixed by '\x'.
     173   *
     174   * Returns: 0 if the entire string was copied, non-zero otherwise.
     175   **/
     176  int blkid_encode_string(const char *str, char *str_enc, size_t len)
     177  {
     178  	size_t i, j;
     179  
     180  	if (!str || !str_enc || !len)
     181  		return -1;
     182  
     183  	for (i = 0, j = 0; str[i] != '\0'; i++) {
     184  		int seqlen;
     185  
     186  		seqlen = utf8_encoded_valid_unichar(&str[i]);
     187  		if (seqlen > 1) {
     188  			if (len-j < (size_t)seqlen)
     189  				goto err;
     190  			memcpy(&str_enc[j], &str[i], seqlen);
     191  			j += seqlen;
     192  			i += (seqlen-1);
     193  		} else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
     194  			if (len-j < 4)
     195  				goto err;
     196  			sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
     197  			j += 4;
     198  		} else {
     199  			if (len-j < 1)
     200  				goto err;
     201  			str_enc[j] = str[i];
     202  			j++;
     203  		}
     204  		if (j+3 >= len)
     205  			goto err;
     206  	}
     207  	if (len-j < 1)
     208  		goto err;
     209  	str_enc[j] = '\0';
     210  	return 0;
     211  err:
     212  	return -1;
     213  }
     214  
     215  /**
     216   * blkid_safe_string:
     217   * @str: input string
     218   * @str_safe: output string
     219   * @len: size of output string
     220   *
     221   * Processing whitespace characters. Allows valid ascii,valid utf8.
     222   * Replace everything else with'_'
     223   *
     224   * Returns: 0 on success or -1 in case of error.
     225   */
     226  int blkid_safe_string(const char *str, char *str_safe, size_t len)
     227  {
     228  	size_t i = 0;
     229  
     230  	if (!str || !str_safe || !len)
     231  		return -1;
     232  
     233  	__normalize_whitespace(
     234  			(const unsigned char *) str, strnlen(str, len),
     235  			(unsigned char *) str_safe, len);
     236  
     237  	while (i < len && str_safe[i] != '\0') {
     238  		int seqsz;
     239  
     240  		/* accept ASCII from ' ' to '~' */
     241  		if (str_safe[i] > 0x20 && str_safe[i] <= 0x7E)
     242  			i++;
     243  
     244  		/* accept hex encoding */
     245  		else if (str_safe[i] == '\\' && str_safe[i+1] == 'x')
     246  			i += 2;
     247  
     248  		/* replace whitespace */
     249  		else if (isspace(str_safe[i]))
     250  			str_safe[i++] = '_';
     251  
     252  		/* accept valid utf8 */
     253  		else if ((seqsz = utf8_encoded_valid_unichar(&str_safe[i])) >= 1)
     254  			i += seqsz;
     255  
     256  		/* everything else is replaced with '_' */
     257  		else
     258  			str_safe[i++] = '_';
     259  	}
     260  
     261  	str_safe[len - 1] = '\0';
     262  	return 0;
     263  }