/* * remove_tablespace_directories: attempt to remove filesystem infrastructure * * Returns TRUE if successful, FALSE if some subdirectory is not empty * * redo indicates we are redoing a drop from XLOG; okay if nothing there */ static bool remove_tablespace_directories(Oid tablespaceoid, bool redo, char *phys) { char *location; DIR *dirdesc; struct dirent *de; char *subfile; struct stat st; char *tempstr; location = (char *) palloc(10 + 10 + 1); sprintf(location, "pg_tblspc/%u", tablespaceoid); /* * If the tablespace location has been removed previously, then we are done. */ if (stat(location, &st) < 0) { ereport(WARNING, (errmsg("directory linked to \"%s\" does not exist", location) )); return true; } /* * Check if the tablespace still contains any files. We try to rmdir each * per-database directory we find in it. rmdir failure implies there are * still files in that subdirectory, so give up. (We do not have to worry * about undoing any already completed rmdirs, since the next attempt to * use the tablespace from that database will simply recreate the * subdirectory via MirroredFileSysObj_JustInTimeDbDirCreate.) * * Since we hold TablespaceCreateLock, no one else should be creating any * fresh subdirectories in parallel. It is possible that new files are * being created within subdirectories, though, so the rmdir call could * fail. Worst consequence is a less friendly error message. */ dirdesc = AllocateDir(location); if (dirdesc == NULL) { if (redo && errno == ENOENT) { pfree(location); return true; } /* else let ReadDir report the error */ } while ((de = ReadDir(dirdesc, location)) != NULL) { /* Note we ignore PG_VERSION for the nonce */ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, "PG_VERSION") == 0) continue; /* Odd... On snow leopard, we get back "/" as a subdir, which is wrong. Ingore it */ if (de->d_name[0] == '/' && de->d_name[1] == '\0') continue; subfile = palloc(strlen(location) + 1 + strlen(de->d_name) + 1); sprintf(subfile, "%s/%s", location, de->d_name); /* This check is just to deliver a friendlier error message */ if (!directory_is_empty(subfile)) { FreeDir(dirdesc); return false; } /* Do the real deed */ if (rmdir(subfile) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", subfile))); pfree(subfile); } FreeDir(dirdesc); /* * Okay, try to unlink PG_VERSION (we allow it to not be there, even in * non-REDO case, for robustness). */ subfile = palloc(strlen(location) + 11 + 1); sprintf(subfile, "%s/PG_VERSION", location); if (unlink(subfile) < 0) { if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", subfile))); } pfree(subfile); /* * Okay, try to remove the symlink. We must however deal with the * possibility that it's a directory instead of a symlink --- this could * happen during WAL replay (see TablespaceCreateDbspace), and it is also * the normal case on Windows. */ if (lstat(location, &st) == 0 && S_ISDIR(st.st_mode)) { if (rmdir(location) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", location))); } else { if (unlink(location) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", location))); } pfree(location); /* Now we have removed all of our linkage to the physical * location; remove the per-segment location that we built at * CreateTablespace() time */ tempstr = palloc(MAXPGPATH); sprintf(tempstr,"%s/seg%d",phys,Gp_segment); if (rmdir(tempstr) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove subdirectory \"%s\": %m", tempstr))); pfree(tempstr); return true; }
/* * destroy_tablespace_directories * * Attempt to remove filesystem infrastructure for the tablespace. * * 'redo' indicates we are redoing a drop from XLOG; in that case we should * not throw an ERROR for problems, just LOG them. The worst consequence of * not removing files here would be failure to release some disk space, which * does not justify throwing an error that would require manual intervention * to get the database running again. * * Returns TRUE if successful, FALSE if some subdirectory is not empty */ static bool destroy_tablespace_directories(Oid tablespaceoid, bool redo) { char *linkloc; char *linkloc_with_version_dir; DIR *dirdesc; struct dirent *de; char *subfile; struct stat st; linkloc_with_version_dir = psprintf("pg_tblspc/%u/%s", tablespaceoid, TABLESPACE_VERSION_DIRECTORY); /* * Check if the tablespace still contains any files. We try to rmdir each * per-database directory we find in it. rmdir failure implies there are * still files in that subdirectory, so give up. (We do not have to worry * about undoing any already completed rmdirs, since the next attempt to * use the tablespace from that database will simply recreate the * subdirectory via TablespaceCreateDbspace.) * * Since we hold TablespaceCreateLock, no one else should be creating any * fresh subdirectories in parallel. It is possible that new files are * being created within subdirectories, though, so the rmdir call could * fail. Worst consequence is a less friendly error message. * * If redo is true then ENOENT is a likely outcome here, and we allow it * to pass without comment. In normal operation we still allow it, but * with a warning. This is because even though ProcessUtility disallows * DROP TABLESPACE in a transaction block, it's possible that a previous * DROP failed and rolled back after removing the tablespace directories * and/or symlink. We want to allow a new DROP attempt to succeed at * removing the catalog entries (and symlink if still present), so we * should not give a hard error here. */ dirdesc = AllocateDir(linkloc_with_version_dir); if (dirdesc == NULL) { if (errno == ENOENT) { if (!redo) ereport(WARNING, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", linkloc_with_version_dir))); /* The symlink might still exist, so go try to remove it */ goto remove_symlink; } else if (redo) { /* in redo, just log other types of error */ ereport(LOG, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return false; } /* else let ReadDir report the error */ } while ((de = ReadDir(dirdesc, linkloc_with_version_dir)) != NULL) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; subfile = psprintf("%s/%s", linkloc_with_version_dir, de->d_name); /* This check is just to deliver a friendlier error message */ if (!redo && !directory_is_empty(subfile)) { FreeDir(dirdesc); pfree(subfile); pfree(linkloc_with_version_dir); return false; } /* remove empty directory */ if (rmdir(subfile) < 0) ereport(redo ? LOG : ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", subfile))); pfree(subfile); } FreeDir(dirdesc); /* remove version directory */ if (rmdir(linkloc_with_version_dir) < 0) { ereport(redo ? LOG : ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc_with_version_dir))); pfree(linkloc_with_version_dir); return false; } /* * Try to remove the symlink. We must however deal with the possibility * that it's a directory instead of a symlink --- this could happen during * WAL replay (see TablespaceCreateDbspace), and it is also the case on * Windows where junction points lstat() as directories. * * Note: in the redo case, we'll return true if this final step fails; * there's no point in retrying it. Also, ENOENT should provoke no more * than a warning. */ remove_symlink: linkloc = pstrdup(linkloc_with_version_dir); get_parent_directory(linkloc); if (lstat(linkloc, &st) == 0 && S_ISDIR(st.st_mode)) { if (rmdir(linkloc) < 0) ereport(redo ? LOG : ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", linkloc))); } #ifdef S_ISLNK else if (S_ISLNK(st.st_mode)) { if (unlink(linkloc) < 0) { int saved_errno = errno; ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR), (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", linkloc))); } } #endif else { /* Refuse to remove anything that's not a directory or symlink */ ereport(redo ? LOG : ERROR, (ERRCODE_SYSTEM_ERROR, errmsg("not a directory or symbolic link: \"%s\"", linkloc))); } pfree(linkloc_with_version_dir); pfree(linkloc); return true; }
/* * remove_tablespace_directories: attempt to remove filesystem infrastructure * * Returns TRUE if successful, FALSE if some subdirectory is not empty * * redo indicates we are redoing a drop from XLOG; okay if nothing there */ static bool remove_tablespace_directories(Oid tablespaceoid, bool redo) { char *location; DIR *dirdesc; struct dirent *de; char *subfile; struct stat st; location = (char *) palloc(10 + 10 + 1); sprintf(location, "pg_tblspc/%u", tablespaceoid); /* * Check if the tablespace still contains any files. We try to rmdir each * per-database directory we find in it. rmdir failure implies there are * still files in that subdirectory, so give up. (We do not have to worry * about undoing any already completed rmdirs, since the next attempt to * use the tablespace from that database will simply recreate the * subdirectory via TablespaceCreateDbspace.) * * Since we hold TablespaceCreateLock, no one else should be creating any * fresh subdirectories in parallel. It is possible that new files are * being created within subdirectories, though, so the rmdir call could * fail. Worst consequence is a less friendly error message. */ dirdesc = AllocateDir(location); if (dirdesc == NULL) { if (redo && errno == ENOENT) { pfree(location); return true; } /* else let ReadDir report the error */ } while ((de = ReadDir(dirdesc, location)) != NULL) { /* Note we ignore PG_VERSION for the nonce */ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, "PG_VERSION") == 0) continue; subfile = palloc(strlen(location) + 1 + strlen(de->d_name) + 1); sprintf(subfile, "%s/%s", location, de->d_name); /* This check is just to deliver a friendlier error message */ if (!directory_is_empty(subfile)) { FreeDir(dirdesc); return false; } /* Do the real deed */ if (rmdir(subfile) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not delete directory \"%s\": %m", subfile))); pfree(subfile); } FreeDir(dirdesc); /* * Okay, try to unlink PG_VERSION (we allow it to not be there, even in * non-REDO case, for robustness). */ subfile = palloc(strlen(location) + 11 + 1); sprintf(subfile, "%s/PG_VERSION", location); if (unlink(subfile) < 0) { if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", subfile))); } pfree(subfile); /* * Okay, try to remove the symlink. We must however deal with the * possibility that it's a directory instead of a symlink --- this could * happen during WAL replay (see TablespaceCreateDbspace), and it is also * the normal case on Windows. */ if (lstat(location, &st) == 0 && S_ISDIR(st.st_mode)) { if (rmdir(location) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove directory \"%s\": %m", location))); } else { if (unlink(location) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", location))); } pfree(location); return true; }
void jscoverage_instrument(const char * source, const char * destination, int verbose, char ** exclude, int num_exclude, char ** no_instrument, int num_no_instrument) { assert(source != NULL); assert(destination != NULL); g_verbose = verbose; /* check if they are the same */ check_same_file(source, destination); /* check if source directory is an ancestor of destination directory */ check_contains_file(source, destination); /* check that the source exists and is a directory */ struct stat buf; xstat(source, &buf); if (! S_ISDIR(buf.st_mode)) { fatal("not a directory: %s", source); } /* if the destination directory exists, check that it is a jscoverage directory */ if (stat(destination, &buf) == 0) { /* it exists */ if (! S_ISDIR(buf.st_mode)) { fatal("not a directory: %s", destination); } if (! directory_is_empty(destination)) { char * expected_file = NULL; if (jscoverage_mozilla) { char * modules_directory = make_path(destination, "modules"); expected_file = make_path(modules_directory, "jscoverage.jsm"); free(modules_directory); } else { expected_file = make_path(destination, "jscoverage.html"); } if (stat(expected_file, &buf) == -1) { fatal("refusing to overwrite directory: %s", destination); } free(expected_file); } } else if (errno == ENOENT) { xmkdir(destination); } else { fatal("cannot stat directory: %s", destination); } /* finally: copy the directory */ struct DirListEntry * list = make_recursive_dir_list(source); for (struct DirListEntry * p = list; p != NULL; p = p->next) { char * s = make_path(source, p->name); char * d = make_path(destination, p->name); /* check if it's on the exclude list */ for (int i = 0; i < num_exclude; i++) { char * x = make_path(source, exclude[i]); if (is_same_file(x, s) || contains_file(x, s)) { free(x); goto cleanup; } free(x); } char * dd = make_dirname(d); mkdirs(dd); free(dd); int instrument_this = 1; /* check if it's on the no-instrument list */ for (int i = 0; i < num_no_instrument; i++) { char * ni = make_path(source, no_instrument[i]); if (is_same_file(ni, s) || contains_file(ni, s)) { instrument_this = 0; } free(ni); } instrument_file(s, d, p->name, instrument_this); cleanup: free(s); free(d); } free_dir_list(list); }
/* * Create a table space * * Only superusers can create a tablespace. This seems a reasonable restriction * since we're determining the system layout and, anyway, we probably have * root if we're doing this kind of activity */ void CreateTableSpace(CreateTableSpaceStmt *stmt) { #ifdef HAVE_SYMLINK Relation rel; Datum values[Natts_pg_tablespace]; char nulls[Natts_pg_tablespace]; HeapTuple tuple; Oid tablespaceoid; char *location; char *linkloc; Oid ownerId; /* validate */ /* don't call this in a transaction block */ PreventTransactionChain((void *) stmt, "CREATE TABLESPACE"); /* Must be super user */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create tablespace \"%s\"", stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); else ownerId = GetUserId(); /* Unix-ify the offered path, and strip any trailing slashes */ location = pstrdup(stmt->location); canonicalize_path(location); /* disallow quotes, else CREATE DATABASE would be at risk */ if (strchr(location, '\'')) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("tablespace location may not contain single quotes"))); /* * Allowing relative paths seems risky * * this also helps us ensure that location is not empty or whitespace */ if (!is_absolute_path(location)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location must be an absolute path"))); /* * Check that location isn't too long. Remember that we're going to append * '/<dboid>/<relid>.<nnn>' (XXX but do we ever form the whole path * explicitly? This may be overly conservative.) */ if (strlen(location) >= (MAXPGPATH - 1 - 10 - 1 - 10 - 1 - 10)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("tablespace location \"%s\" is too long", location))); /* * Disallow creation of tablespaces named "pg_xxx"; we reserve this * namespace for system purposes. */ if (!allowSystemTableMods && IsReservedName(stmt->tablespacename)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("unacceptable tablespace name \"%s\"", stmt->tablespacename), errdetail("The prefix \"pg_\" is reserved for system tablespaces."))); /* * Check that there is no other tablespace by this name. (The unique * index would catch this anyway, but might as well give a friendlier * message.) */ if (OidIsValid(get_tablespace_oid(stmt->tablespacename))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("tablespace \"%s\" already exists", stmt->tablespacename))); /* * Insert tuple into pg_tablespace. The purpose of doing this first is to * lock the proposed tablename against other would-be creators. The * insertion will roll back if we find problems below. */ rel = heap_open(TableSpaceRelationId, RowExclusiveLock); MemSet(nulls, ' ', Natts_pg_tablespace); values[Anum_pg_tablespace_spcname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->tablespacename)); values[Anum_pg_tablespace_spcowner - 1] = ObjectIdGetDatum(ownerId); values[Anum_pg_tablespace_spclocation - 1] = DirectFunctionCall1(textin, CStringGetDatum(location)); nulls[Anum_pg_tablespace_spcacl - 1] = 'n'; tuple = heap_formtuple(rel->rd_att, values, nulls); tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); heap_freetuple(tuple); /* Record dependency on owner */ recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId); /* * Attempt to coerce target directory to safe permissions. If this fails, * it doesn't exist or has the wrong owner. */ if (chmod(location, 0700) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not set permissions on directory \"%s\": %m", location))); /* * Check the target directory is empty. */ if (!directory_is_empty(location)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("directory \"%s\" is not empty", location))); /* * Create the PG_VERSION file in the target directory. This has several * purposes: to make sure we can write in the directory, to prevent * someone from creating another tablespace pointing at the same directory * (the emptiness check above will fail), and to label tablespace * directories by PG version. */ set_short_version(location); /* * All seems well, create the symlink */ linkloc = (char *) palloc(10 + 10 + 1); sprintf(linkloc, "pg_tblspc/%u", tablespaceoid); if (symlink(location, linkloc) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create symbolic link \"%s\": %m", linkloc))); /* Record the filesystem change in XLOG */ { xl_tblspc_create_rec xlrec; XLogRecData rdata[2]; xlrec.ts_id = tablespaceoid; rdata[0].data = (char *) &xlrec; rdata[0].len = offsetof(xl_tblspc_create_rec, ts_path); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) location; rdata[1].len = strlen(location) + 1; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; (void) XLogInsert(RM_TBLSPC_ID, XLOG_TBLSPC_CREATE, rdata); } pfree(linkloc); pfree(location); /* We keep the lock on pg_tablespace until commit */ heap_close(rel, NoLock); #else /* !HAVE_SYMLINK */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif /* HAVE_SYMLINK */ }