Hidden Files in Perforce

Published by Marco on in Perforce

We keep our source code in Perforce, an extremely powerful SCM (source control manager). It retains multiple revisions of files and manages integrations between “branches”, or diverging streams of similar code. Read/write permissions for paths within the depot can be restricted per user.

Recently, we needed to get the history of a particular file to see which changes had been applied to it over the course of the last year. Instead of the approximately 20 revisions we expected, we saw only one. The comment for the revision indicated that the last operation on this file was that it had been renamed to its current location.

Renaming

A rename in Perforce consists of two operations: the file is integrated from the source location to the target location and then deleted from the source location. That is, Perforce maintains the history of a file as multiple resources in the depot whose histories are linked by integrations. The command to retrieve file history accepts a flag, -i, which indicates that the server should follow these previous branches, returning the history for those files as well (as it is understood to be a single resource).

To continue with the example above, we expected to see the revisions for the file at its previous location (before it was renamed) listed as part of our file’s history.[1] Perplexed, we tried to browse to the file location from which it was branched, only to discover that we couldn’t even get to the folder for it, much less the file itself. In fact, the entire folder hierarchy right up to the root had been deleted and was no longer available. In this case, Perforce clients let you show deleted files, to ease restoring accidentally deleted resources, but that didn’t help either. Explicitly including the folder in the workspace mapping yielded nothing.

Obliterating

We were stumped … and starting to get worried that we had obliterated that entire part of the depot instead of just deleting it.

By default, Perforce never forgets anything. Any change you make to a file in the depot is recorded to a changelist and stored with the file’s meta-data. This includes, naturally, deleted files. If, however, files, or specific revisions, are really no longer needed, Perforce can remove them—along with their metadata—permanently. See the obliterate documentation for more information.

Before we started trying to figure out who to blame for the premature obliteration, we checked the depot on the server itself—only to discover that all of the files were still there, exactly where we expected them to be. Using the command line client and the maintenance user on the server, we created a temporary workspace and synced an older revision (an undeleted one) of the file to it. Opening the file revealed an intact source file.

Perforce Clients

Next, we figured that the GUI clients might have a problem, though it was hard to believe that it was a bug. To be sure, we used the command line client on the original machine to get the file using the exact same commands as on the server and, lo and behold, we saw:

“protected namespace − access denied.”

Aha!

Protecting depots

As mentioned above, Perforce has a security system through which all data requests are filtered. Users only receive information about files to which they have “read” access. Imagine that the user has rights to //depot/projects/sample/, but not //depot/old_projects/sample/, where the files used to reside.

Under these conditions, a command to retrieve file history with branches would be processed as follows:

  1. The client makes a request for the file history, p4 filelog -i -L //depot/projects/sample/versions.txt
  2. The server processes the command, putting together the file list for the result, which includes listings from //depot/projects/sample/ and //depot/old_projects/sample/.
  3. The server filters the list according to what the user can see, returning only the entries from //depot/projects/sample/.[2]

This explains why the GUI showed only one revision in the file history, instead of 20. It also explains why the original projects folder didn’t appear in the depot view in any GUI client. Only the command-line client indicated a protection problem because a forbidden path was explicitly requested.

Preventing Confusion

Is there any way to improve the clients so that a user knows when content is being denied? Is this even desired, from a security standpoint? When viewing the depot tree, the user should not be made aware that there are entire folders being blocked from view by protections—that’s a clear security breach. In that case, the user has not made an explicit request for data that has been blocked; the user has made a request for all available data, which implicitly means all readable data.

When viewing the revisions for a particular file, the user has made an explicit request for the file’s history, including previous branches. If any of those branches are blocked, the client has no indication that this happened because the server filtered the list before returning it. In contrast to the other situation, the user did explicitly request information from a forbidden path and should be notified of it. However, the request returns a list of paths rather than a single file (as was the case with the command-line client above). It would not be appropriate to return only an error message if a particular branch of a file’s history has been blocked.

A more elegant solution would deliver the available history (as it does now), but also an error message indicating that part of the result was blocked due to protection restrictions (again, as shown above). This way, a user knows that the protection mechanism blocked part of the information and can ask the administrator if there is a configuration problem (as was the case with our server).

For changelist requests, there is also no indication that part of the filelist was blocked, leaving a user to wonder why there are files missing from a changelist[3]. As with file history, the request for a changelist is explicit and should indicate a partial error. On the other hand, if all files in a changelist are blocked, the server should just return an error message (blocking the description as well). If multiple lists are requested, the blocked changelist should not appear at all.

Conclusions

With current versions of Perforce, users need to be aware that their rights are potentially restricted and remember to check with their administrator before assuming that information has been lost. Administrators need to be aware that setting protections does not restrict users from seeing all changelist descriptions, regardless of whether they are allowed to see files in that list or not.


[1] The P4Win client includes a Display branching history checkbox, but all the subsequently displayed revisions are folded into a flat list, wherein branch boundaries are recognizable only by a reversion of the revision number to one. Use the P4V client for a more comfortable view, in which each file location (or branch) in the history has its own tree node, which can be opened and closed individually.
[2] We also verified that this server-side filtering applies everywhere, including changelists. If a user requests a changelist in which all paths are blocked, the changelist appears empty.
[3] Granted, this applies only if the user is examining older changelists, submitted before protection permissions were changed. This can be confusing because the user may remember checking in a particular file, but doesn’t see it now, which then looks like a bug in the server.

Using p4d/p4v/p4 2006.1 & p4win 2006.2