Reworked dance of config files rewriting (lots of debug prints) ENGINE-451
authorClaudio Luck <claudio.luck@pep.foundation>
Sun, 12 Aug 2018 00:05:48 +0200
branchENGINE-451
changeset 28109ae82b72ca33
parent 2809 45ebfd77285d
child 2811 4bb07fc31b93
Reworked dance of config files rewriting (lots of debug prints)
src/pgp_gpg.c
     1.1 --- a/src/pgp_gpg.c	Sat Aug 11 22:58:23 2018 +0200
     1.2 +++ b/src/pgp_gpg.c	Sun Aug 12 00:05:48 2018 +0200
     1.3 @@ -27,8 +27,96 @@
     1.4  
     1.5  // This is in response to ENGINE-427. We should NOT be aiming to keep this here.
     1.6  bool quickfix_config(stringlist_t* keys, const char* config_file_path) {
     1.7 -    return false;
     1.8 -#if 0
     1.9 +    static char buf[MAX_LINELENGTH];
    1.10 +    size_t num_keys = stringlist_length(keys);
    1.11 +
    1.12 +    // This function does:
    1.13 +    // 1. find a non-existent backup name numbered from 0 to 99 (otherwise fails)
    1.14 +    // 2. read the original file and meanwhile write the backup copy
    1.15 +    // 3. write the new config file to a temporary file in the same directory
    1.16 +    // 4. rename the temp file replacing the original config file
    1.17 +    // 5. on Windows remove the left-overs
    1.18 +
    1.19 +    /* Find a suitable backup file name, without trashing previous ones */
    1.20 +    char* backup_file_path = NULL;
    1.21 +    size_t backup_file_path_baselen = strlen(config_file_path);
    1.22 +    FILE *backup_file = 0;
    1.23 +    int ret;
    1.24 +    int found = 0;
    1.25 +    int i;
    1.26 +    char* temp_config_file_path = NULL;
    1.27 +    char* s = NULL;
    1.28 +    stringlist_t* _k;
    1.29 +    stringlist_t* lines = new_stringlist(NULL);
    1.30 +    FILE *f;
    1.31 +    FILE *temp_config_file;
    1.32 +    stringlist_t* cur_string;
    1.33 +    bool status = false;
    1.34 +
    1.35 +#ifdef WIN32
    1.36 +    WIN32_FIND_DATA FindFileData;
    1.37 +    HANDLE handle;
    1.38 +
    1.39 +    const char* line_end = "\r\n";
    1.40 +#else
    1.41 +    const char* line_end = "\n";
    1.42 +#endif
    1.43 +
    1.44 +    // If we bork it up somehow, we don't go beyond 100 tries...
    1.45 +    for (int nr = 0; nr < 99; nr++) {
    1.46 +        backup_file_path = (char*)calloc(backup_file_path_baselen + 12, 1);  // .99.pep.old\0
    1.47 +        ret = snprintf(backup_file_path, backup_file_path_baselen + 12,
    1.48 +                                        "%s.%d.pep.bkp", config_file_path, nr);
    1.49 +        fprintf(stderr, "backup_file_path = '%s' (ret = %d)\n", backup_file_path, ret);
    1.50 +        assert(ret >= 0);  // snprintf(2)
    1.51 +        if (ret < 0) {
    1.52 +            goto quickfix_error;  // frees backup_file_path
    1.53 +        }
    1.54 +
    1.55 +#ifdef WIN32
    1.56 +        // The fopen(.., "x") is not documented on Windows (fopen_s actually respects it, but...).
    1.57 +        // So we make an extra check for the existence of the file. This introduces a possible
    1.58 +        // race-condition, but it has little effect even if we incur into it.
    1.59 +        handle = FindFirstFile(backup_file_path, &FindFileData);
    1.60 +        if (handle != INVALID_HANDLE_VALUE) {
    1.61 +                fprintf(stderr, "Windows says file may exist %s\n", backup_file_path);
    1.62 +                FindClose(handle);
    1.63 +                free(backup_file_path);
    1.64 +                backup_file_path = NULL;
    1.65 +                continue;
    1.66 +        }
    1.67 +        FindClose(handle);
    1.68 +
    1.69 +        backup_file = Fopen(backup_file_path, "wb");
    1.70 +#else
    1.71 +        backup_file = Fopen(backup_file_path, "wbx");    // the 'x' is important
    1.72 +#endif
    1.73 +        fprintf(stderr, "backup_file = %d\n", backup_file);
    1.74 +        if (backup_file <= 0) {
    1.75 +            fprintf(stderr, "backup_file open fail\n");
    1.76 +            free(backup_file_path);
    1.77 +            backup_file_path = NULL;
    1.78 +            continue;
    1.79 +        }
    1.80 +        break;
    1.81 +    }
    1.82 +
    1.83 +    if (!backup_file_path)
    1.84 +        goto quickfix_error;
    1.85 +
    1.86 +    if (backup_file <= 0)
    1.87 +        goto quickfix_error;
    1.88 +
    1.89 +    // Open original file, parse it, and meanwhile write a backup copy
    1.90 +
    1.91 +    f = Fopen(config_file_path, "rb");
    1.92 +
    1.93 +    if (f == NULL)
    1.94 +        goto quickfix_error;
    1.95 +
    1.96 +    ret = Fprintf(backup_file, "# Backup created by pEp.%s"
    1.97 +                               "# If GnuPG and pEp work smoothly this file may safely be removed.%s%s",
    1.98 +                               line_end, line_end, line_end);
    1.99  
   1.100      // Go through every line in the file
   1.101      while ((s = Fgets(buf, MAX_LINELENGTH, f))) {
   1.102 @@ -36,11 +124,23 @@
   1.103          str_ptr_and_bit* found_keys = (str_ptr_and_bit*)(calloc(num_keys, sizeof(str_ptr_and_bit)));
   1.104          int num_found_keys = 0;
   1.105  
   1.106 +        ret = Fprintf(backup_file, "%s", s);
   1.107 +        assert(ret >= 0);
   1.108 +        if (ret < 0) {
   1.109 +            free(found_keys);
   1.110 +            found_keys = NULL;
   1.111 +            goto quickfix_error;
   1.112 +        }
   1.113 +
   1.114          char* rest;
   1.115          char* line_token = strtok_r(s, "\r\n", &rest);
   1.116 +
   1.117          if (!line_token)
   1.118              line_token = s;
   1.119 -            
   1.120 +
   1.121 +        if (*line_token == '\n' || *line_token == '\r')
   1.122 +            line_token = "";
   1.123 +
   1.124          if (*line_token == '#' || *line_token == '\0') {
   1.125              stringlist_add(lines, strdup(line_token));
   1.126              continue;
   1.127 @@ -61,6 +161,7 @@
   1.128                      case '-':
   1.129                      case ':':
   1.130                      case '/':
   1.131 +                    case '\\':
   1.132                          notkey = true;
   1.133                          break;
   1.134                      default:
   1.135 @@ -74,6 +175,7 @@
   1.136                      case '-':
   1.137                      case ':':
   1.138                      case '/':
   1.139 +                    case '\\':
   1.140                          notkey = true;
   1.141                          break;
   1.142                      default:
   1.143 @@ -93,11 +195,11 @@
   1.144              if (!notkey) {
   1.145                  // Ok, it's not just the key with a null terminator. So...
   1.146                  // add a pointer to the key to the list from this string
   1.147 -                found_keys[num_found_keys].key = keypos; 	
   1.148 +                found_keys[num_found_keys].key = keypos;
   1.149                  found_keys[num_found_keys].bit = i;
   1.150                  num_found_keys++;
   1.151              }
   1.152 -            
   1.153 +
   1.154              // Check to see if there are more annoying occurences of this 
   1.155              // key in the string
   1.156              for (keypos = strstr(nextpos, _k->value); 
   1.157 @@ -109,6 +211,7 @@
   1.158                      case '-':
   1.159                      case ':':
   1.160                      case '/':
   1.161 +                    case '\\':
   1.162                          notkey = true;
   1.163                          break;
   1.164                      default:
   1.165 @@ -120,6 +223,7 @@
   1.166                          case '-':
   1.167                          case ':':
   1.168                          case '/':
   1.169 +                        case '\\':
   1.170                              notkey = true;
   1.171                              break;
   1.172                          default:
   1.173 @@ -132,12 +236,12 @@
   1.174                  if (num_found_keys >= num_keys)
   1.175                      found_keys = (str_ptr_and_bit*)realloc(found_keys, (num_found_keys + 1) * sizeof(str_ptr_and_bit));
   1.176                      
   1.177 -                found_keys[num_found_keys].key = keypos; 	
   1.178 +                found_keys[num_found_keys].key = keypos;
   1.179                  found_keys[num_found_keys].bit = i;
   1.180                  num_found_keys++;     
   1.181              }
   1.182          }
   1.183 -        
   1.184 +
   1.185          if (!only_key_on_line) {
   1.186              if (num_found_keys == 0)
   1.187                  stringlist_add(lines, strdup(line_token));        
   1.188 @@ -183,7 +287,116 @@
   1.189          free(found_keys);
   1.190          found_keys = NULL;
   1.191      } // End of file
   1.192 +
   1.193 +    // Now do the failsafe writing dance
   1.194 +
   1.195 +    ret = Fclose(f);
   1.196 +    fprintf(stderr, "close config file ret = %d\n", ret);
   1.197 +    assert(ret == 0);
   1.198 +    if (ret != 0)
   1.199 +        goto quickfix_error;
   1.200 +
   1.201 +    ret = Fclose(backup_file);
   1.202 +    fprintf(stderr, "close backup config file ret = %d\n", ret);
   1.203 +    assert(ret == 0);
   1.204 +    if (ret != 0)
   1.205 +        goto quickfix_error;
   1.206 +
   1.207 +    // 2. Write the new config file to a temporary file in the same directory
   1.208 +
   1.209 +    assert(backup_file_path_baselen != NULL);
   1.210 +
   1.211 +    temp_config_file_path = (char*)calloc(backup_file_path_baselen + 8, 1);  // .XXXXXX\0
   1.212 +    ret = snprintf(temp_config_file_path, backup_file_path_baselen + 8, "%s.XXXXXX", config_file_path);
   1.213 +    fprintf(stderr, "temp_config_file_path = '%s' (ret = %d)\n", temp_config_file_path, ret);
   1.214 +    assert(ret == NULL);
   1.215 +    if (!ret)
   1.216 +        goto quickfix_error;
   1.217 +
   1.218 +    int temp_config_filedesc = Mkstemp(temp_config_file_path);
   1.219 +    fprintf(stderr, "temp_config_filedesc '%s' (fd: %d, errno: %d)\n", temp_config_file_path, temp_config_filedesc, errno);
   1.220 +    if (temp_config_filedesc == -1)
   1.221 +        goto quickfix_error;
   1.222 +
   1.223 +    temp_config_file = fdopen(temp_config_filedesc, "w");    // no "b" in fdopen() is documentend, use freopen()
   1.224 +    fprintf(stderr, "temp_config_file '%s' (fd=%d)\n", temp_config_filedesc, temp_config_file);
   1.225 +    assert(temp_config_file != NULL);
   1.226 +    if (temp_config_file == NULL)
   1.227 +        goto quickfix_error;
   1.228 +
   1.229 +    temp_config_file = Freopen(config_file_path, "wb", temp_config_file);
   1.230 +    fprintf(stderr, "reopen: temp_config_file = '%d'\n", temp_config_file);
   1.231 +    assert(temp_config_file != NULL);
   1.232 +    if (temp_config_file == NULL)
   1.233 +        goto quickfix_error;
   1.234 +
   1.235 +    ret = Fprintf(temp_config_file, "# File re-created by pEp%s"
   1.236 +                                    "# See backup in '%s'%s%s", line_end,
   1.237 +                                                                backup_file_path,
   1.238 +                                                                line_end, line_end);
   1.239 +    assert(ret >= 0);
   1.240 +    if (ret < 0)
   1.241 +        goto quickfix_error;
   1.242 +        
   1.243 +    for (cur_string = lines; cur_string; cur_string = cur_string->next) {
   1.244 +        assert(cur_string->value != NULL);
   1.245 +        ret = Fprintf(temp_config_file, "%s%s", cur_string->value, line_end);
   1.246 +        fprintf(stderr, "Fprintf = '%s' (ret=%d)\n", cur_string->value, ret);
   1.247 +        assert(ret >= 0);
   1.248 +        if (ret < 0)
   1.249 +            goto quickfix_error;
   1.250 +    }
   1.251 +
   1.252 +    ret = Fclose(temp_config_file);
   1.253 +    assert(ret == 0);
   1.254 +    if (ret != 0)
   1.255 +        goto quickfix_error;
   1.256 +
   1.257 +#ifdef WIN32
   1.258 +    ret = !(0 == ReplaceFile(config_file_path, temp_config_file_path, NULL, 0, NULL, NULL));
   1.259 +    fprintf(stderr, "rename temp_config_file_path = '%s' (ret=%d)\n", temp_config_file_path, ret);
   1.260 +    assert(ret == 0);
   1.261 +    if (ret != 0)
   1.262 +        goto quickfix_error;
   1.263 +
   1.264 +    ret = unlink(temp_config_file_path);
   1.265 +    fprintf(stderr, "unlink temp_config_file_path = '%s' (ret=%d)\n", temp_config_file_path, ret);
   1.266 +#else
   1.267 +    ret = rename(config_file_path, temp_config_file_path);
   1.268 +    fprintf(stderr, "rename temp_config_file_path = '%s' (ret=%d)\n", temp_config_file_path, ret);
   1.269  #endif
   1.270 +    assert(ret == 0);
   1.271 +    if (ret != 0)
   1.272 +        goto quickfix_error;
   1.273 +
   1.274 +    free(temp_config_file_path);
   1.275 +    temp_config_file_path = NULL;
   1.276 +
   1.277 +    status = true;
   1.278 +
   1.279 +    free(backup_file_path);
   1.280 +
   1.281 +    goto quickfix_success;
   1.282 +
   1.283 +
   1.284 +quickfix_error:
   1.285 +
   1.286 +    assert(status == false);
   1.287 +
   1.288 +    free(backup_file_path);
   1.289 +
   1.290 +
   1.291 +quickfix_success:
   1.292 +
   1.293 +    assert(found_keys == NULL);
   1.294 +
   1.295 +    if (temp_config_file_path)
   1.296 +        free(temp_config_file_path);
   1.297 +
   1.298 +    free_stringlist(lines);
   1.299 +
   1.300 +    return status;
   1.301 +
   1.302  }
   1.303  
   1.304  static bool ensure_config_values(stringlist_t *keys, stringlist_t *values, const char* config_file_path)
   1.305 @@ -206,7 +419,7 @@
   1.306      line_end = "\n";
   1.307  #endif    
   1.308  
   1.309 -    FILE *f = Fopen(config_file_path, "r");
   1.310 +    FILE *f = Fopen(config_file_path, "rb");
   1.311      if (f == NULL && errno == ENOMEM)
   1.312          return false;
   1.313  
   1.314 @@ -247,10 +460,14 @@
   1.315  
   1.316          if (!s && ferror(f))
   1.317              return false;
   1.318 -        f = Freopen(config_file_path, "a", f);
   1.319 +        f = Freopen(config_file_path, "ab", f);
   1.320      }
   1.321      else {
   1.322 -        f = Fopen(config_file_path, "w");
   1.323 +#ifdef WIN32
   1.324 +        f = Fopen(config_file_path, "wb");
   1.325 +#else
   1.326 +        f = Fopen(config_file_path, "wbx");
   1.327 +#endif
   1.328      }
   1.329  
   1.330      assert(f);