tsdbFS.c 13.4 KB
Newer Older
H
Hongze Cheng 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
 *
 * This program is free software: you can use, redistribute, and/or modify
 * it under the terms of the GNU Affero General Public License, version 3
 * or later ("AGPL"), as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
H
Hongze Cheng 已提交
14 15
 */

H
Hongze Cheng 已提交
16
#include "inc/tsdbFS.h"
H
Hongze Cheng 已提交
17

H
Hongze Cheng 已提交
18 19 20 21 22 23 24 25 26 27
#define TSDB_FS_EDIT_MIN TSDB_FS_EDIT_COMMIT
#define TSDB_FS_EDIT_MAX (TSDB_FS_EDIT_MERGE + 1)

enum {
  TSDB_FS_STATE_NONE = 0,
  TSDB_FS_STATE_OPEN,
  TSDB_FS_STATE_EDIT,
  TSDB_FS_STATE_CLOSE,
};

H
Hongze Cheng 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40
typedef enum {
  TSDB_FCURRENT = 1,
  TSDB_FCURRENT_C,
  TSDB_FCURRENT_M,
} EFCurrentT;

static const char *gCurrentFname[] = {
    [TSDB_FCURRENT] = "current.json",
    [TSDB_FCURRENT_C] = "current.json.0",
    [TSDB_FCURRENT_M] = "current.json.1",
};

static int32_t create_fs(STsdb *pTsdb, STFileSystem **ppFS) {
H
Hongze Cheng 已提交
41 42
  ppFS[0] = taosMemoryCalloc(1, sizeof(*ppFS[0]));
  if (ppFS[0] == NULL) {
H
Hongze Cheng 已提交
43 44 45
    return TSDB_CODE_OUT_OF_MEMORY;
  }

H
Hongze Cheng 已提交
46 47 48 49 50
  ppFS[0]->cstate = taosArrayInit(16, sizeof(STFileSet));
  ppFS[0]->nstate = taosArrayInit(16, sizeof(STFileSet));
  if (ppFS[0]->cstate == NULL || ppFS[0]->nstate == NULL) {
    taosArrayDestroy(ppFS[0]->nstate);
    taosArrayDestroy(ppFS[0]->cstate);
H
Hongze Cheng 已提交
51 52 53
    taosMemoryFree(ppFS[0]);
    return TSDB_CODE_OUT_OF_MEMORY;
  }
H
Hongze Cheng 已提交
54

H
Hongze Cheng 已提交
55
  ppFS[0]->pTsdb = pTsdb;
H
Hongze Cheng 已提交
56
  ppFS[0]->state = TSDB_FS_STATE_NONE;
H
Hongze Cheng 已提交
57
  tsem_init(&ppFS[0]->canEdit, 0, 1);
H
Hongze Cheng 已提交
58
  ppFS[0]->nextEditId = 0;
H
Hongze Cheng 已提交
59

H
Hongze Cheng 已提交
60 61 62
  return 0;
}

H
Hongze Cheng 已提交
63 64 65 66 67 68 69
static int32_t destroy_fs(STFileSystem **ppFS) {
  if (ppFS[0] == NULL) return 0;
  taosArrayDestroy(ppFS[0]->nstate);
  taosArrayDestroy(ppFS[0]->cstate);
  tsem_destroy(&ppFS[0]->canEdit);
  taosMemoryFree(ppFS[0]);
  ppFS[0] = NULL;
H
Hongze Cheng 已提交
70 71 72
  return 0;
}

H
Hongze Cheng 已提交
73
static int32_t current_fname(STsdb *pTsdb, char *fname, EFCurrentT ftype) {
H
Hongze Cheng 已提交
74 75 76 77 78 79 80 81
  if (pTsdb->pVnode->pTfs) {
    snprintf(fname,                                   //
             TSDB_FILENAME_LEN,                       //
             "%s%s%s%s%s",                            //
             tfsGetPrimaryPath(pTsdb->pVnode->pTfs),  //
             TD_DIRSEP,                               //
             pTsdb->path,                             //
             TD_DIRSEP,                               //
H
Hongze Cheng 已提交
82
             gCurrentFname[ftype]);
H
Hongze Cheng 已提交
83 84 85 86 87 88
  } else {
    snprintf(fname,              //
             TSDB_FILENAME_LEN,  //
             "%s%s%s",           //
             pTsdb->path,        //
             TD_DIRSEP,          //
H
Hongze Cheng 已提交
89
             gCurrentFname[ftype]);
H
Hongze Cheng 已提交
90 91 92 93
  }
  return 0;
}

H
Hongze Cheng 已提交
94
static int32_t fs_to_json_str(STFileSystem *pFS, char **ppData) {
H
Hongze Cheng 已提交
95 96 97 98 99 100 101 102 103
  int32_t code = 0;
  int32_t lino;

  cJSON *pJson = cJSON_CreateObject();
  if (pJson == NULL) {
    return TSDB_CODE_OUT_OF_MEMORY;
  }

  /* format version */
H
Hongze Cheng 已提交
104 105 106
  TSDB_CHECK_NULL(                        //
      cJSON_AddNumberToObject(pJson,      //
                              "version",  //
H
Hongze Cheng 已提交
107 108 109 110 111 112 113
                              1 /* TODO */),
      code,   //
      lino,   //
      _exit,  //
      TSDB_CODE_OUT_OF_MEMORY);

  /* next edit id */
H
Hongze Cheng 已提交
114 115 116
  TSDB_CHECK_NULL(                        //
      cJSON_AddNumberToObject(pJson,      //
                              "edit id",  //
H
Hongze Cheng 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
                              pFS->nextEditId),
      code,   //
      lino,   //
      _exit,  //
      TSDB_CODE_OUT_OF_MEMORY);

  /* file sets */
  cJSON *aFileSetJson;
  TSDB_CHECK_NULL(                                                //
      aFileSetJson = cJSON_AddArrayToObject(pJson, "file sets"),  //
      code,                                                       //
      lino,                                                       //
      _exit,                                                      //
      TSDB_CODE_OUT_OF_MEMORY);

H
Hongze Cheng 已提交
132 133
  for (int32_t i = 0; i < taosArrayGetSize(pFS->nstate); i++) {
    struct STFileSet *pFileSet = taosArrayGet(pFS->nstate, i);
H
Hongze Cheng 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

    code = tsdbFileSetToJson(aFileSetJson, pFileSet);
    TSDB_CHECK_CODE(code, lino, _exit);
  }

  ppData[0] = cJSON_Print(pJson);
  if (ppData[0] == NULL) {
    code = TSDB_CODE_OUT_OF_MEMORY;
    TSDB_CHECK_CODE(code, lino, _exit);
  }

_exit:
  cJSON_Delete(pJson);
  if (code) {
    tsdbError("vgId:%d %s failed at line %d since %s",  //
              TD_VID(pFS->pTsdb->pVnode),               //
              __func__,                                 //
              lino,                                     //
              tstrerror(code));
  }
  return code;
}

H
Hongze Cheng 已提交
157
static int32_t fs_from_json_str(const char *pData, STFileSystem *pFS) {
H
Hongze Cheng 已提交
158 159 160
  int32_t code = 0;
  int32_t lino;

H
Hongze Cheng 已提交
161
  ASSERTS(0, "TODO: Not implemented yet");
H
Hongze Cheng 已提交
162 163 164 165 166

_exit:
  return code;
}

H
Hongze Cheng 已提交
167
static int32_t save_fs_to_file(STFileSystem *pFS, const char *fname) {
H
Hongze Cheng 已提交
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
  int32_t code = 0;
  int32_t lino;
  char   *pData = NULL;

  // to json string
  code = fs_to_json_str(pFS, &pData);
  TSDB_CHECK_CODE(code, lino, _exit);

  TdFilePtr fd = taosOpenFile(fname,                //
                              TD_FILE_WRITE         //
                                  | TD_FILE_CREATE  //
                                  | TD_FILE_TRUNC);
  if (fd == NULL) {
    code = TAOS_SYSTEM_ERROR(code);
    TSDB_CHECK_CODE(code, lino, _exit);
  }

  int64_t n = taosWriteFile(fd, pData, strlen(pData) + 1);
  if (n < 0) {
    code = TAOS_SYSTEM_ERROR(code);
    taosCloseFile(&fd);
    TSDB_CHECK_CODE(code, lino, _exit);
  }

  if (taosFsyncFile(fd) < 0) {
    code = TAOS_SYSTEM_ERROR(code);
    taosCloseFile(&fd);
    TSDB_CHECK_CODE(code, lino, _exit);
  }

  taosCloseFile(&fd);

_exit:
  if (code) {
    tsdbError("vgId:%d %s failed at line %d since %s",  //
              TD_VID(pFS->pTsdb->pVnode),               //
              __func__,                                 //
              lino,                                     //
              tstrerror(code));
  } else {
    tsdbDebug("vgId:%d %s success",        //
              TD_VID(pFS->pTsdb->pVnode),  //
              __func__);
  }
  if (pData) {
    taosMemoryFree(pData);
  }
  return code;
H
Hongze Cheng 已提交
216 217
}

H
Hongze Cheng 已提交
218
static int32_t load_fs_from_file(const char *fname, STFileSystem *pFS) {
H
Hongze Cheng 已提交
219 220 221 222
  ASSERTS(0, "TODO: Not implemented yet");
  return 0;
}

H
Hongze Cheng 已提交
223
static int32_t commit_edit(STFileSystem *pFS, tsdb_fs_edit_t etype) {
H
Hongze Cheng 已提交
224 225 226 227
  int32_t code;
  char    ofname[TSDB_FILENAME_LEN];
  char    nfname[TSDB_FILENAME_LEN];

H
Hongze Cheng 已提交
228 229
  current_fname(pFS->pTsdb, nfname, TSDB_FCURRENT);
  current_fname(pFS->pTsdb, ofname, etype == TSDB_FS_EDIT_COMMIT ? TSDB_FCURRENT_C : TSDB_FCURRENT_M);
H
Hongze Cheng 已提交
230 231 232 233 234 235 236 237 238

  code = taosRenameFile(ofname, nfname);
  if (code) {
    code = TAOS_SYSTEM_ERROR(code);
    return code;
  }

  ASSERTS(0, "TODO: Do changes to pFS");

H
Hongze Cheng 已提交
239 240 241
  return 0;
}

H
Hongze Cheng 已提交
242
static int32_t abort_edit(STFileSystem *pFS, tsdb_fs_edit_t etype) {
H
Hongze Cheng 已提交
243 244 245
  int32_t code;
  char    fname[TSDB_FILENAME_LEN];

H
Hongze Cheng 已提交
246
  current_fname(pFS->pTsdb, fname, etype == TSDB_FS_EDIT_COMMIT ? TSDB_FCURRENT_C : TSDB_FCURRENT_M);
H
Hongze Cheng 已提交
247 248 249 250 251 252 253

  code = taosRemoveFile(fname);
  if (code) code = TAOS_SYSTEM_ERROR(code);

  return code;
}

H
Hongze Cheng 已提交
254
static int32_t scan_file_system(STFileSystem *pFS) {
H
Hongze Cheng 已提交
255 256 257 258
  // ASSERTS(0, "TODO: Not implemented yet");
  return 0;
}

H
Hongze Cheng 已提交
259
static int32_t scan_and_schedule_merge(STFileSystem *pFS) {
H
Hongze Cheng 已提交
260
  // ASSERTS(0, "TODO: Not implemented yet");
H
Hongze Cheng 已提交
261 262 263
  return 0;
}

H
Hongze Cheng 已提交
264 265 266 267 268 269
static int32_t update_fs_if_needed(STFileSystem *pFS) {
  // TODO
  return 0;
}

static int32_t open_fs(STFileSystem *pFS, int8_t rollback) {
H
Hongze Cheng 已提交
270
  int32_t code = 0;
H
Hongze Cheng 已提交
271
  int32_t lino = 0;
H
Hongze Cheng 已提交
272 273
  STsdb  *pTsdb = pFS->pTsdb;

H
Hongze Cheng 已提交
274 275
  code = update_fs_if_needed(pFS);
  TSDB_CHECK_CODE(code, lino, _exit)
H
Hongze Cheng 已提交
276

H
Hongze Cheng 已提交
277 278 279
  char fCurrent[TSDB_FILENAME_LEN];
  char cCurrent[TSDB_FILENAME_LEN];
  char mCurrent[TSDB_FILENAME_LEN];
H
Hongze Cheng 已提交
280

H
Hongze Cheng 已提交
281 282 283
  current_fname(pTsdb, fCurrent, TSDB_FCURRENT);
  current_fname(pTsdb, cCurrent, TSDB_FCURRENT_C);
  current_fname(pTsdb, mCurrent, TSDB_FCURRENT_C);
H
Hongze Cheng 已提交
284

H
Hongze Cheng 已提交
285 286 287
  if (taosCheckExistFile(fCurrent)) {  // current.json exists
    code = load_fs_from_file(fCurrent, pFS);
    TSDB_CHECK_CODE(code, lino, _exit);
H
Hongze Cheng 已提交
288

H
Hongze Cheng 已提交
289 290 291 292 293 294 295
    // check current.json.commit existence
    if (taosCheckExistFile(cCurrent)) {
      if (rollback) {
        code = commit_edit(pFS, TSDB_FS_EDIT_COMMIT);
        TSDB_CHECK_CODE(code, lino, _exit);
      } else {
        code = abort_edit(pFS, TSDB_FS_EDIT_COMMIT);
H
Hongze Cheng 已提交
296 297
        TSDB_CHECK_CODE(code, lino, _exit);
      }
H
Hongze Cheng 已提交
298 299 300 301 302
    }

    // check current.json.t existence
    if (taosCheckExistFile(mCurrent)) {
      code = abort_edit(pFS, TSDB_FS_EDIT_MERGE);
H
Hongze Cheng 已提交
303 304
      TSDB_CHECK_CODE(code, lino, _exit);
    }
H
Hongze Cheng 已提交
305 306 307
  } else {
    code = save_fs_to_file(pFS, fCurrent);
    TSDB_CHECK_CODE(code, lino, _exit);
H
Hongze Cheng 已提交
308 309
  }

H
Hongze Cheng 已提交
310 311 312 313 314 315
  code = scan_file_system(pFS);
  TSDB_CHECK_CODE(code, lino, _exit);

  code = scan_and_schedule_merge(pFS);
  TSDB_CHECK_CODE(code, lino, _exit);

H
Hongze Cheng 已提交
316 317
_exit:
  if (code) {
H
Hongze Cheng 已提交
318
    tsdbError("vgId:%d %s failed at line %d since %s", TD_VID(pTsdb->pVnode), __func__, lino, tstrerror(code));
H
Hongze Cheng 已提交
319
  } else {
H
Hongze Cheng 已提交
320
    tsdbInfo("vgId:%d %s success", TD_VID(pTsdb->pVnode), __func__);
H
Hongze Cheng 已提交
321
  }
H
Hongze Cheng 已提交
322 323 324
  return 0;
}

H
Hongze Cheng 已提交
325
static int32_t close_file_system(STFileSystem *pFS) {
H
Hongze Cheng 已提交
326
  ASSERTS(0, "TODO: Not implemented yet");
H
Hongze Cheng 已提交
327 328
  return 0;
}
H
Hongze Cheng 已提交
329

H
Hongze Cheng 已提交
330
static int32_t apply_edit(STFileSystem *pFS) {
H
Hongze Cheng 已提交
331
  int32_t code = 0;
H
Hongze Cheng 已提交
332
  ASSERTS(0, "TODO: Not implemented yet");
H
Hongze Cheng 已提交
333
  return code;
H
Hongze Cheng 已提交
334 335
}

H
Hongze Cheng 已提交
336
static int32_t fset_cmpr_fn(const struct STFileSet *pSet1, const struct STFileSet *pSet2) {
H
Hongze Cheng 已提交
337 338 339 340 341 342 343 344
  if (pSet1->fid < pSet2->fid) {
    return -1;
  } else if (pSet1->fid > pSet2->fid) {
    return 1;
  }
  return 0;
}

H
Hongze Cheng 已提交
345
static int32_t edit_fs(STFileSystem *pFS, const SArray *aFileOp) {
H
Hongze Cheng 已提交
346 347 348
  int32_t code = 0;
  int32_t lino;

H
Hongze Cheng 已提交
349
  taosArrayClearEx(pFS->nstate, NULL /* TODO */);
H
Hongze Cheng 已提交
350 351 352

  // TODO: copy current state to new state

H
Hongze Cheng 已提交
353 354
  for (int32_t iop = 0; iop < taosArrayGetSize(aFileOp); iop++) {
    struct SFileOp *pOp = taosArrayGet(aFileOp, iop);
H
Hongze Cheng 已提交
355

H
Hongze Cheng 已提交
356
    struct STFileSet tmpSet = {.fid = pOp->fid};
H
Hongze Cheng 已提交
357 358

    int32_t idx = taosArraySearchIdx(  //
H
Hongze Cheng 已提交
359
        pFS->nstate,                   //
H
Hongze Cheng 已提交
360 361 362 363
        &tmpSet,                       //
        (__compar_fn_t)fset_cmpr_fn,   //
        TD_GE);

H
Hongze Cheng 已提交
364
    struct STFileSet *pSet;
H
Hongze Cheng 已提交
365 366
    if (idx < 0) {
      pSet = NULL;
H
Hongze Cheng 已提交
367
      idx = taosArrayGetSize(pFS->nstate);
H
Hongze Cheng 已提交
368
    } else {
H
Hongze Cheng 已提交
369
      pSet = taosArrayGet(pFS->nstate, idx);
H
Hongze Cheng 已提交
370 371 372 373 374 375 376 377 378
    }

    if (pSet == NULL || pSet->fid != pOp->fid) {
      ASSERTS(pOp->op == TSDB_FOP_CREATE, "BUG: Invalid file operation");
      TSDB_CHECK_CODE(                                //
          code = tsdbFileSetCreate(pOp->fid, &pSet),  //
          lino,                                       //
          _exit);

H
Hongze Cheng 已提交
379
      if (taosArrayInsert(pFS->nstate, idx, pSet) == NULL) {
H
Hongze Cheng 已提交
380 381 382 383 384 385
        code = TSDB_CODE_OUT_OF_MEMORY;
        TSDB_CHECK_CODE(code, lino, _exit);
      }
    }

    // do opration on file set
H
Hongze Cheng 已提交
386
    TSDB_CHECK_CODE(                        //
H
Hongze Cheng 已提交
387
        code = tsdbFileSetEdit(pSet, pOp),  //
H
Hongze Cheng 已提交
388 389
        lino,                               //
        _exit);
H
Hongze Cheng 已提交
390 391
  }

H
Hongze Cheng 已提交
392 393
  // TODO: write new state to file

H
Hongze Cheng 已提交
394
_exit:
H
Hongze Cheng 已提交
395 396 397
  return 0;
}

H
Hongze Cheng 已提交
398
int32_t tsdbOpenFileSystem(STsdb *pTsdb, STFileSystem **ppFS, int8_t rollback) {
H
Hongze Cheng 已提交
399 400 401
  int32_t code;
  int32_t lino;

H
Hongze Cheng 已提交
402
  code = create_fs(pTsdb, ppFS);
H
Hongze Cheng 已提交
403 404
  TSDB_CHECK_CODE(code, lino, _exit);

H
Hongze Cheng 已提交
405
  code = open_fs(ppFS[0], rollback);
H
Hongze Cheng 已提交
406
  TSDB_CHECK_CODE(code, lino, _exit)
H
Hongze Cheng 已提交
407 408 409

_exit:
  if (code) {
H
Hongze Cheng 已提交
410
    tsdbError("vgId:%d %s failed at line %d since %s", TD_VID(pTsdb->pVnode), __func__, lino, tstrerror(code));
H
Hongze Cheng 已提交
411
    destroy_fs(ppFS);
H
Hongze Cheng 已提交
412
  } else {
H
Hongze Cheng 已提交
413
    tsdbInfo("vgId:%d %s success", TD_VID(pTsdb->pVnode), __func__);
H
Hongze Cheng 已提交
414 415 416 417
  }
  return 0;
}

H
Hongze Cheng 已提交
418
int32_t tsdbCloseFileSystem(STFileSystem **ppFS) {
H
Hongze Cheng 已提交
419 420
  if (ppFS[0] == NULL) return 0;
  close_file_system(ppFS[0]);
H
Hongze Cheng 已提交
421
  destroy_fs(ppFS);
H
Hongze Cheng 已提交
422 423
  return 0;
}
H
Hongze Cheng 已提交
424

H
Hongze Cheng 已提交
425
int32_t tsdbFileSystemEditBegin(STFileSystem *pFS, const SArray *aFileOp, tsdb_fs_edit_t etype) {
H
Hongze Cheng 已提交
426
  int32_t code = 0;
H
Hongze Cheng 已提交
427
  int32_t lino;
H
Hongze Cheng 已提交
428 429
  char    fname[TSDB_FILENAME_LEN];

H
Hongze Cheng 已提交
430
  current_fname(pFS->pTsdb, fname, etype == TSDB_FS_EDIT_COMMIT ? TSDB_FCURRENT_C : TSDB_FCURRENT_M);
H
Hongze Cheng 已提交
431

H
Hongze Cheng 已提交
432
  tsem_wait(&pFS->canEdit);
H
Hongze Cheng 已提交
433

H
Hongze Cheng 已提交
434 435 436 437 438 439 440 441 442
  TSDB_CHECK_CODE(                   //
      code = edit_fs(pFS, aFileOp),  //
      lino,                          //
      _exit);

  TSDB_CHECK_CODE(                         //
      code = save_fs_to_file(pFS, fname),  //
      lino,                                //
      _exit);
H
Hongze Cheng 已提交
443 444 445 446 447 448 449 450

_exit:
  if (code) {
    tsdbError("vgId:%d %s failed at line %d since %s",  //
              TD_VID(pFS->pTsdb->pVnode),               //
              __func__,                                 //
              lino,                                     //
              tstrerror(code));
H
Hongze Cheng 已提交
451 452 453 454 455
  } else {
    tsdbInfo("vgId:%d %s done, etype:%d",  //
             TD_VID(pFS->pTsdb->pVnode),   //
             __func__,                     //
             etype);
H
Hongze Cheng 已提交
456 457 458 459
  }
  return code;
}

H
Hongze Cheng 已提交
460
int32_t tsdbFileSystemEditCommit(STFileSystem *pFS, tsdb_fs_edit_t etype) {
H
Hongze Cheng 已提交
461
  int32_t code = commit_edit(pFS, etype);
H
Hongze Cheng 已提交
462
  tsem_post(&pFS->canEdit);
H
Hongze Cheng 已提交
463
  if (code) {
H
Hongze Cheng 已提交
464 465 466
    tsdbError("vgId:%d %s failed since %s",  //
              TD_VID(pFS->pTsdb->pVnode),    //
              __func__,                      //
H
Hongze Cheng 已提交
467
              tstrerror(code));
H
Hongze Cheng 已提交
468 469 470 471 472
  } else {
    tsdbInfo("vgId:%d %s done, etype:%d",  //
             TD_VID(pFS->pTsdb->pVnode),   //
             __func__,                     //
             etype);
H
Hongze Cheng 已提交
473 474 475 476
  }
  return code;
}

H
Hongze Cheng 已提交
477
int32_t tsdbFileSystemEditAbort(STFileSystem *pFS, tsdb_fs_edit_t etype) {
H
Hongze Cheng 已提交
478
  int32_t code = abort_edit(pFS, etype);
H
Hongze Cheng 已提交
479
  if (code) {
H
Hongze Cheng 已提交
480 481 482 483 484
    tsdbError("vgId:%d %s failed since %s, etype:%d",  //
              TD_VID(pFS->pTsdb->pVnode),              //
              __func__,                                //
              tstrerror(code),                         //
              etype);
H
Hongze Cheng 已提交
485
  } else {
H
Hongze Cheng 已提交
486
  }
H
Hongze Cheng 已提交
487
  tsem_post(&pFS->canEdit);
H
Hongze Cheng 已提交
488 489
  return code;
}