Esempio n. 1
0
/*
 * Create an fsentry-based directory listing (similar to opendir / readdir).
 * Dir should not contain trailing '/'. Use an empty string for the current
 * directory (not "."!).
 */
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
{
	wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
	WIN32_FIND_DATAW fdata;
	HANDLE h;
	int wlen;
	struct fsentry *list, **phead;
	DWORD err;

	/* convert name to UTF-16 and check length */
	if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH,
			dir->len, MAX_PATH - 2, core_long_paths)) < 0)
		return NULL;

	/*
	 * append optional '\' and wildcard '*'. Note: we need to use '\' as
	 * Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
	 */
	if (wlen)
		pattern[wlen++] = '\\';
	pattern[wlen++] = '*';
	pattern[wlen] = 0;

	/* open find handle */
	h = FindFirstFileW(pattern, &fdata);
	if (h == INVALID_HANDLE_VALUE) {
		err = GetLastError();
		errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
		return NULL;
	}

	/* allocate object to hold directory listing */
	list = fsentry_alloc(NULL, dir->name, dir->len);

	/* walk directory and build linked list of fsentry structures */
	phead = &list->next;
	do {
		*phead = fseentry_create_entry(list, &fdata);
		phead = &(*phead)->next;
	} while (FindNextFileW(h, &fdata));

	/* remember result of last FindNextFile, then close find handle */
	err = GetLastError();
	FindClose(h);

	/* return the list if we've got all the files */
	if (err == ERROR_NO_MORE_FILES)
		return list;

	/* otherwise free the list and return error */
	fsentry_release(list);
	errno = err_win_to_posix(err);
	return NULL;
}
Esempio n. 2
0
/*
 * Allocate and initialize an fsentry from a FILE_FULL_DIR_INFORMATION structure.
 */
static struct fsentry *fseentry_create_entry(struct fscache *cache, struct fsentry *list,
		PFILE_FULL_DIR_INFORMATION fdata)
{
	char buf[MAX_PATH * 3];
	int len;
	struct fsentry *fse;

	len = xwcstoutfn(buf, ARRAY_SIZE(buf), fdata->FileName, fdata->FileNameLength / sizeof(wchar_t));

	fse = fsentry_alloc(cache, list, buf, len);

	/*
	 * On certain Windows versions, host directories mapped into
	 * Windows Containers ("Volumes", see https://docs.docker.com/storage/volumes/)
	 * look like symbolic links, but their targets are paths that
	 * are valid only in kernel mode.
	 *
	 * Let's work around this by detecting that situation and
	 * telling Git that these are *not* symbolic links.
	 */
	if (fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
	    fdata->EaSize == IO_REPARSE_TAG_SYMLINK &&
	    sizeof(buf) > (list ? list->len + 1 : 0) + fse->len + 1 &&
	    is_inside_windows_container()) {
		size_t off = 0;
		if (list) {
			memcpy(buf, list->name, list->len);
			buf[list->len] = '/';
			off = list->len + 1;
		}
		memcpy(buf + off, fse->name, fse->len);
		buf[off + fse->len] = '\0';
	}

	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
			fdata->EaSize, buf);
	fse->st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
			fdata->EndOfFile.LowPart | (((off_t)fdata->EndOfFile.HighPart) << 32);
	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime), &(fse->st_atim));
	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime), &(fse->st_mtim));
	filetime_to_timespec((FILETIME *)&(fdata->CreationTime), &(fse->st_ctim));

	return fse;
}
Esempio n. 3
0
File: fscache.c Progetto: guban/git
/*
 * Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
 */
static struct fsentry *fseentry_create_entry(struct fsentry *list,
		const WIN32_FIND_DATAW *fdata)
{
	char buf[MAX_PATH * 3];
	int len;
	struct fsentry *fse;
	len = xwcstoutf(buf, fdata->cFileName, ARRAY_SIZE(buf));

	fse = fsentry_alloc(list, buf, len);

	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes,
			fdata->dwReserved0);
	fse->st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
			fdata->nFileSizeLow | (((off_t) fdata->nFileSizeHigh) << 32);
	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->st_atim));
	filetime_to_timespec(&(fdata->ftLastWriteTime), &(fse->st_mtim));
	filetime_to_timespec(&(fdata->ftCreationTime), &(fse->st_ctim));

	return fse;
}
Esempio n. 4
0
/*
 * Looks up or creates a cache entry for the specified key.
 */
static struct fsentry *fscache_get(struct fscache *cache, struct fsentry *key)
{
	struct fsentry *fse;
	int dir_not_found;

	cache->fscache_requests++;
	/* check if entry is in cache */
	fse = hashmap_get(&cache->map, key, NULL);
	if (fse) {
		if (fse->st_mode)
			fsentry_addref(fse);
		else
			fse = NULL; /* non-existing directory */
		return fse;
	}
	/* if looking for a file, check if directory listing is in cache */
	if (!fse && key->list) {
		fse = hashmap_get(&cache->map, key->list, NULL);
		if (fse) {
			/*
			 * dir entry without file entry, or dir does not
			 * exist -> file doesn't exist
			 */
			errno = ENOENT;
			return NULL;
		}
	}

	/* create the directory listing */
	fse = fsentry_create_list(cache, key->list ? key->list : key, &dir_not_found);

	/* leave on error (errno set by fsentry_create_list) */
	if (!fse) {
		if (dir_not_found && key->list) {
			/*
			 * Record that the directory does not exist (or is
			 * empty, which for all practical matters is the same
			 * thing as far as fscache is concerned).
			 */
			fse = fsentry_alloc(cache, key->list->list,
					    key->list->name, key->list->len);
			fse->st_mode = 0;
			hashmap_add(&cache->map, fse);
		}
		return NULL;
	}

	/* add directory listing to the cache */
	cache->fscache_misses++;
	fscache_add(cache, fse);

	/* lookup file entry if requested (fse already points to directory) */
	if (key->list)
		fse = hashmap_get(&cache->map, key, NULL);

	if (fse && !fse->st_mode)
		fse = NULL; /* non-existing directory */

	/* return entry or ENOENT */
	if (fse)
		fsentry_addref(fse);
	else
		errno = ENOENT;

	return fse;
}
Esempio n. 5
0
/*
 * Create an fsentry-based directory listing (similar to opendir / readdir).
 * Dir should not contain trailing '/'. Use an empty string for the current
 * directory (not "."!).
 */
static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir,
					   int *dir_not_found)
{
	wchar_t pattern[MAX_LONG_PATH];
	NTSTATUS status;
	IO_STATUS_BLOCK iosb;
	PFILE_FULL_DIR_INFORMATION di;
	HANDLE h;
	int wlen;
	struct fsentry *list, **phead;
	DWORD err;

	*dir_not_found = 0;

	/* convert name to UTF-16 and check length */
	if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH,
			dir->len, MAX_PATH - 2, core_long_paths)) < 0)
		return NULL;

	/* handle CWD */
	if (!wlen) {
		wlen = GetCurrentDirectoryW(ARRAY_SIZE(pattern), pattern);
		if (!wlen || wlen >= ARRAY_SIZE(pattern)) {
			errno = wlen ? ENAMETOOLONG : err_win_to_posix(GetLastError());
			return NULL;
		}
	}

	h = CreateFileW(pattern, FILE_LIST_DIRECTORY,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
	if (h == INVALID_HANDLE_VALUE) {
		err = GetLastError();
		*dir_not_found = 1; /* or empty directory */
		errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
		trace_printf_key(&trace_fscache, "fscache: error(%d) '%.*s'\n",
						 errno, dir->len, dir->name);
		return NULL;
	}

	/* allocate object to hold directory listing */
	list = fsentry_alloc(cache, NULL, dir->name, dir->len);
	list->st_mode = S_IFDIR;

	/* walk directory and build linked list of fsentry structures */
	phead = &list->next;
	status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer,
		sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE);
	if (!NT_SUCCESS(status)) {
		/*
		 * NtQueryDirectoryFile returns STATUS_INVALID_PARAMETER when
		 * asked to enumerate an invalid directory (ie it is a file
		 * instead of a directory).  Verify that is the actual cause
		 * of the error.
		*/
		if (status == STATUS_INVALID_PARAMETER) {
			DWORD attributes = GetFileAttributesW(pattern);
			if (!(attributes & FILE_ATTRIBUTE_DIRECTORY))
				status = ERROR_DIRECTORY;
		}
		goto Error;
	}
	di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer);
	for (;;) {

		*phead = fseentry_create_entry(cache, list, di);
		phead = &(*phead)->next;

		/* If there is no offset in the entry, the buffer has been exhausted. */
		if (di->NextEntryOffset == 0) {
			status = NtQueryDirectoryFile(h, NULL, 0, 0, &iosb, cache->buffer,
				sizeof(cache->buffer), FileFullDirectoryInformation, FALSE, NULL, FALSE);
			if (!NT_SUCCESS(status)) {
				if (status == STATUS_NO_MORE_FILES)
					break;
				goto Error;
			}

			di = (PFILE_FULL_DIR_INFORMATION)(cache->buffer);
			continue;
		}

		/* Advance to the next entry. */
		di = (PFILE_FULL_DIR_INFORMATION)(((PUCHAR)di) + di->NextEntryOffset);
	}

	CloseHandle(h);
	return list;

Error:
	errno = (status == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(status);
	trace_printf_key(&trace_fscache, "fscache: error(%d) unable to query directory contents '%.*s'\n",
		errno, dir->len, dir->name);
	CloseHandle(h);
	fsentry_release(list);
	return NULL;
}
Esempio n. 6
0
File: fscache.c Progetto: guban/git
/*
 * Looks up or creates a cache entry for the specified key.
 */
static struct fsentry *fscache_get(struct fsentry *key)
{
	struct fsentry *fse, *future, *waiter;
	int dir_not_found;

	EnterCriticalSection(&mutex);
	/* check if entry is in cache */
	fse = fscache_get_wait(key);
	if (fse) {
		if (fse->st_mode)
			fsentry_addref(fse);
		else
			fse = NULL; /* non-existing directory */
		LeaveCriticalSection(&mutex);
		return fse;
	}
	/* if looking for a file, check if directory listing is in cache */
	if (!fse && key->list) {
		fse = fscache_get_wait(key->list);
		if (fse) {
			LeaveCriticalSection(&mutex);
			/*
			 * dir entry without file entry, or dir does not
			 * exist -> file doesn't exist
			 */
			errno = ENOENT;
			return NULL;
		}
	}

	/* add future entry to indicate that we're loading it */
	future = key->list ? key->list : key;
	future->next = NULL;
	future->refcnt = 0;
	hashmap_add(&map, future);

	/* create the directory listing (outside mutex!) */
	LeaveCriticalSection(&mutex);
	fse = fsentry_create_list(future, &dir_not_found);
	EnterCriticalSection(&mutex);

	/* remove future entry and signal waiting threads */
	hashmap_remove(&map, future, NULL);
	waiter = future->next;
	while (waiter) {
		HANDLE h = waiter->hwait;
		waiter = waiter->next;
		SetEvent(h);
	}

	/* leave on error (errno set by fsentry_create_list) */
	if (!fse) {
		if (dir_not_found && key->list) {
			/*
			 * Record that the directory does not exist (or is
			 * empty, which for all practical matters is the same
			 * thing as far as fscache is concerned).
			 */
			fse = fsentry_alloc(key->list->list,
					    key->list->name, key->list->len);
			fse->st_mode = 0;
			hashmap_add(&map, fse);
		}
		LeaveCriticalSection(&mutex);
		return NULL;
	}

	/* add directory listing to the cache */
	fscache_add(fse);

	/* lookup file entry if requested (fse already points to directory) */
	if (key->list)
		fse = hashmap_get(&map, key, NULL);

	if (fse && !fse->st_mode)
		fse = NULL; /* non-existing directory */

	/* return entry or ENOENT */
	if (fse)
		fsentry_addref(fse);
	else
		errno = ENOENT;

	LeaveCriticalSection(&mutex);
	return fse;
}